A renomeação de um método preserva o encapsulamento?


9

Eu estava lendo esta página , sobre quando os getters / setters são justificados, e o OP forneceu o seguinte exemplo de código:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

A resposta aceita declara:

A propósito, no seu exemplo, eu daria à classe Fridgeos métodos putCheese()e takeCheese(), em vez de get_cheese() e set_cheese(). Então você ainda teria encapsulamento.

Como o encapsulamento é preservado renomeando-o de get / set para putCheese()/ takeCheese()Você obviamente está obtendo / configurando um valor, então por que não simplesmente deixá-lo como get / set?

Na mesma resposta, também afirma:

Ter getters e setters, por si só, não quebra o encapsulamento. O que quebra o encapsulamento é adicionar automaticamente um getter e um setter para cada membro de dados (todos os campos, em linguagem java), sem pensar em nada.

Nesse caso, temos apenas uma variável, cheesee você pode levar e devolver o queijo à geladeira, portanto, um par de get / set é justificado.


7
putCheeseadicionaria queijo ao refrigerador e takeCheeseo removeria - essas são abstrações orientadas a domínios (de nível superior), em vez de getters e setters de campos de objetos (que são abstrações de programação de computador (de baixo nível)).
Erik Eidt

Respostas:


17

Eu acho que você está perdendo o ponto. Não está dizendo que você deve renomear o setter e o getter, mas ter métodos que adicionem e removem itens da geladeira. ie

public class Fridge
{
    private int numberOfCheeseSlices;

    public void AddCheeseSlices(int n)
    {
         if(n < 0) { 
             throw new Exception("you cant add negative cheese!");
         }
         if(numberOfCheeseSlices + n > this.capacityOfFridge) { 
             throw new Exception("TOO MUCH CHEESE!!");
         }
         ..etc
         this.numberOfCheeseSlices += n;
    }
}

Agora a variável privada está encapsulada. Não posso simplesmente defini-lo como quiser, só posso adicionar e remover fatias de queijo da geladeira usando os métodos, que por sua vez garantem que quaisquer regras de lógica de negócios de queijo que eu queira sejam aplicadas.


2
Certo, mas você ainda pode deixá-lo como setCheese()tem a mesma lógica no setter. De qualquer maneira, para mim, você está tentando ocultar o fato de estar usando um setter renomeando o método, mas obviamente seu getter / definindo algo.
QueenSvetlana

21
@QueenSvetlana não é um setter. É uma operação. Você está dizendo à geladeira "aqui está outro queijo" e está adicionando à sua contagem interna de queijo encapsulado. Um levantador diria "agora você tem 5 queijos". Estas são operações funcionalmente diferentes, não apenas nomes diferentes.
Ant

11
você poderia chamá-lo de setCheese, mas isso seria confuso, pois não definiria o valor do queijo.
Ewan

6
Observe que há um erro de excesso de número inteiro aqui. Se já houver mais de 0 queijo, AddCheese(Integer.MAX_VALUE)deve falhar, mas não irá.
user253751

3
que seria totalmente coberto na seção ..etc
Ewan

5

Getters e setters quebram o encapsulamento toda vez , por definição. O que se pode argumentar é que às vezes precisamos fazer isso. Com isso fora do caminho, aqui estão minhas respostas:

Como o encapsulamento é preservado renomeando-o de get / set para putCheese () / takeCheese () Você obviamente está obtendo / configurando um valor, então por que não simplesmente deixá-lo como get / set?

A diferença está na semântica, ou seja, o significado do que você escreve. Encapsulamento não é apenas proteger, mas esconder o estado interno. O estado interno nem deve ser conhecido fora, mas o objeto deve oferecer métodos relevantes aos negócios (às vezes chamados de "comportamento") para manipular / usar esse estado.

Assim, gete setsão termos técnicos e parecem ter nada a ver com o domínio "geladeira", ao mesmo tempo pute takepode realmente fazer algum sentido.

No entanto , se faz putou não takefaz sentido realmente depende dos requisitos e não pode ser objetivamente julgado. Veja o próximo ponto:

Nesse caso, temos apenas uma variável, cheese, e você pode levar e devolver o queijo à geladeira, portanto, um par de get / set é justificado.

Isso não pode ser objetivamente determinado sem mais contexto. Seu exemplo contém um go_shopping()método em outro lugar. Se é para isso que Fridgeserve, do que não precisa getou set, o que precisa é Fridge.go_shopping(). Dessa forma, você tem um método derivado dos requisitos, todos os "dados" necessários são locais e você tem um comportamento real , em vez de apenas uma estrutura de dados velada.

Lembre-se, você não está construindo um genérico, reutilizável Fridge. Você está construindo um apenas Fridgepara seus requisitos. Qualquer esforço gasto para torná-lo mais do que precisa é realmente um desperdício.


10
Getters and setters break encapsulation every single time- Isso é um pouco forte demais para o meu gosto. Os métodos (incluindo caçadores e caçadores) têm o mesmo objetivo que os portões de um concerto: permitem entrada e saída ordenada do local.
Robert Harvey

8
"Getters e setters quebram o encapsulamento toda vez, por definição" - por qual definição? Eu também tenho um problema com isso, porque getters e setters são apenas parte da interface pública, como qualquer outro membro público; eles somente quebram o encapsulamento se expuserem detalhes de implementação considerados internos por design (ou seja, por decisão do programador (ou de uma equipe)). O fato de getters e setters serem frequentemente adicionados aleatoriamente, com o controle de acoplamento sendo uma reflexão tardia, é outra questão.
Filip Milovanović

2
@ FilipMilovanović Fair point. Como mencionei na resposta, "encapsulamento" é usado para ocultar objetos internos (== estado do objeto == variáveis ​​de instância). Getters e setters publicam objetos internamente. Portanto, por essas definições, eles estão em conflito perpétuo. Agora, se às vezes precisamos quebrar o encapsulamento é uma questão diferente, que deve ser tratada separadamente. Para deixar claro, dizendo que setters / getters sempre quebram o encapsulamento, não estou dizendo que sempre é "errado", se isso ajuda.
Robert Bräutigam

5
getters e setters não "quebram o encapsulamento literalmente sempre". Os getters e setters geralmente nem se referem diretamente aos membros diretamente, realizam algum tipo de operação ou são definidos exclusivamente, você pode ter apenas um getter ou apenas um setter. Ele quebra o encapsulamento quando getters e setters não fazem nada além de acesso a membros e são definidos para os mesmos campos. Mesmo isso pode ser necessário para mantenedores de bibliotecas que não desejam interromper ABIs / APIs com alterações internas e evitar a recompilação do cliente.
whn

4
Ok, getters e setter apenas quebram o encapsulamento 99% do tempo. Feliz? E, expondo o tipo do objeto encapsulado, eles definitivamente quebram o encapsulamento. Depois de ter um public int getFoo(), é quase impossível, na prática, mudar para outro tipo.
user949300

3

Quase tudo isso mostra um mal-entendido fundamental do encapsulamento e como ele se aplica.

A resposta inicial de que você estava quebrando o encapsulamento está errada. Talvez seu aplicativo precise definir o valor do queijo na geladeira em vez de aumentar / diminuir ou adicionar / remover. Além disso, não é semântica, não importa como você a chama, se você precisar acessar e / ou alterar atributos, não quebrará o encapsulamento, fornecendo-os. Finalmente, o encapsulamento não se trata realmente de "ocultar", trata-se de controlar o acesso ao estado e aos valores que não precisam ser públicos ou manipulados fora da classe, ao mesmo tempo em que é concedido àqueles que deveriam e executando a tarefa disponibilizada internamente.

Um getter ou setter não quebra o encapsulamento quando há uma necessidade legítima de obter ou definir um valor. É por isso que os métodos podem ser tornados públicos.

Encapsulamento é manter os dados e os métodos que modificam esses dados diretamente juntos em um local lógico, a classe.

Neste caso em particular, é claramente necessário alterar o valor do queijo na aplicação. Independentemente de como isso é feito, via get / set ou add / remove, desde que os métodos estejam encapsulados na classe, você está seguindo o estilo orientado a objetos.

Para esclarecimento, darei um exemplo de como o encapsulamento é quebrado, fornecendo acesso, independentemente do nome do método ou da execução lógica.

Digamos que sua geladeira tenha uma "vida útil", apenas alguns carrapatos antes que a geladeira não esteja mais operacional (por uma questão de argumento, a geladeira não pode ser reparada). Logicamente, não há como um usuário (ou o restante do seu aplicativo) conseguir alterar esse valor. Deve ser privado. Seria visível apenas através, digamos, de um atributo público diferente conhecido como "isWorking". Quando a vida útil expirar, internamente, a geladeira estará funcionando como falsa.

A execução da contagem regressiva da vida útil e o acionamento do interruptor isWorking são todos internos à geladeira, nada fora poderia / deveria ser capaz de afetar o processo. O isWorking deve estar visível apenas, portanto, um getter não quebra o encapsulamento. No entanto, a adição de acessadores para elementos do processo de vida útil quebraria seu encapsulamento.

Como a maioria das coisas, a definição de encapsulamento não é literal, é relativa. Você deve conseguir ver o X fora da classe? Você deveria poder mudar Y? Tudo o que se aplica ao seu objeto está nesta classe ou a funcionalidade está espalhada por várias classes?


Vamos olhar pragmaticamente. Você está dizendo que o encapsulamento não é quebrado se o código for planejado dessa maneira ou se houver um motivo "legítimo". Isso basicamente significa que nunca podemos dizer que o encapsulamento está quebrado, porque o desenvolvedor precisa apenas dizer que "pretendia" dessa maneira, ou que havia um motivo "legítimo". Portanto, essa definição é basicamente inútil, não é?
Robert Bräutigam

Não, eu digo que o encapsulamento não é quebrado simplesmente fornecendo acesso. E não tem nada a ver com o que um programador diz, mas com as necessidades do aplicativo. Um aplicativo precisa ter acesso para manipular os objetos, o encapsulamento é sobre como controlar o acesso. Um aplicativo pode precisar "definir" um atributo de um objeto, mas a lógica e / ou os cálculos necessários para executar essa operação "conjunto" devem ser encapsulados na classe de objeto.
user343330

Eu acho que é aqui que diferimos fundamentalmente, você diz: "Um aplicativo pode precisar" definir "o atributo de um objeto ...". Acho que não. A escolha de um design que envolva a configuração ou a obtenção de um atributo depende do desenvolvedor. É uma escolha! Existem várias maneiras de codificar algo, nem todas envolvem a obtenção ou a configuração de valores de variáveis ​​de instância.
Robert Bräutigam

Pode ser ... Normalmente, o design é baseado em atender a uma necessidade, seja ele escolhido pelo programador ou baseado no ambiente de negócios. Em ambos os casos, se o aplicativo tiver uma necessidade fundamental de manipular um valor que faça parte de um objeto, será necessário tornar essa funcionalidade acessível de alguma forma. Fornecer um ponto de entrada para fazer o trabalho não está quebrando o encapsulamento. Pegue um aplicativo de vendas. Em algum momento, a transação deve calcular o imposto, fazendo que no objeto de transação seria mantê-lo encapsulado, mas o aplicativo deve ser capaz de estado transaction.CalcTax
user343330

Setters são maus exemplos por causa de sua simplicidade, pense em termos de uma função que faz muito mais trabalho que resulta em um atributo de objeto sendo atualizado versus algo fora da classe fazendo o trabalho e depois dizendo object.attribute = x. O primeiro é um bom encapsulamento enquanto fornece o acesso apropriado, o segundo não. Quanto aos getters, o aplicativo precisa conhecer exatamente essa parte de um objeto para fazer outra coisa que não possa estar contida na classe de objeto? se sim, a exposição não interrompe o seu encapsulamento. Se não, então encapsulá-lo na classe de objeto
user343330

1

Não é apenas renomear um método. Os dois métodos funcionam de maneira diferente.

(Imagine isso em sua mente)

get_cheese e set_cheese expõe o queijo. O putCheese () e o takeCheese () mantêm o queijo oculto e se encarregam de gerenciá-lo e dar ao usuário uma maneira de lidar com isso. O observador não vê o queijo, apenas vê dois métodos para manipulá-lo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.