Como o encapsulamento é usado para segurança?

8

Estou aprendendo OOP. Eu estudei muito sobre encapsulamento, mas quanto mais eu leio, mais fico confuso.

Entendo que ocultamos (tornando privados) dados e os expomos ao usuário da classe (outros desenvolvedores) como propriedades ou métodos. Eu também entendo por encapsulamento que escondemos detalhes.

Em um artigo (http://www.csharp-station.com/Tutorial/CSharp/lesson19), li:

Resumo do artigo

Ao projetar um objeto, você deve pensar em como os outros poderiam usá-lo. No melhor cenário, qualquer programa usando o objeto seria bem projetado e o código nunca mudaria. No entanto, a realidade é que os programas mudam frequentemente e, em um ambiente de equipe, muitas pessoas tocam o mesmo código uma vez ou outra. Portanto, é benéfico considerar o que pode dar errado, bem como a imagem primitiva de como o objeto deve ser usado.

No caso do objeto BankAccount, examine a situação em que o código fora do seu objeto pode acessar um campo Valor decimal ou um campo String de nome do cliente. No momento em que o código é escrito, tudo funcionaria bem. No entanto, mais tarde no ciclo de desenvolvimento, você percebe que o objeto BankAccount deve acompanhar um int CustomerID em vez de string CustomerName, porque você não deseja duplicar os relacionamentos entre informações (ou algum outro motivo válido para alterar a definição de estado interno) . Essas alterações causam um efeito ondulatório no seu código, porque ele foi criado para usar a classe BankAccount, como originalmente projetado (com CustomerName sendo uma sequência), e agora você deve alterar o código que acessa esse estado em todo o aplicativo.

O princípio do Encapsulation, orientado a objetos, ajuda a evitar esses problemas, permitindo ocultar o estado interno e o acesso abstrato a ele por meio de membros do tipo como métodos, propriedades e indexadores. O encapsulamento ajuda a reduzir o acoplamento entre objetos e aumenta a capacidade de manutenção do seu código.

Pergunta Como o encapsulamento ajudará ao fazer alterações no código e em seus efeitos de ondulação. Para um membro de dados, se eu alterar seu tipo de int para float (mesmo se estiver expondo essa propriedade usando), precisarei alterar o tipo de variável em que já estou usando esse código.

Por favor, me guie como o encapsulamento ajudará com essas mudanças.

Obrigado por esta ajuda e orientação.

haansi
fonte

Respostas:

8

Como o encapsulamento ajudará ao fazer alterações no código e a partir de seus efeitos ondulantes. Para um membro de dados, se eu alterar seu tipo de int para float (mesmo se estiver expondo essa propriedade usando), precisarei alterar o tipo de variável em que já estou usando esse código.

O benefício do encapsulamento é que ele permite alterar a implementação interna sem quebrar o código do cliente. Não o protege se você decidir alterar a interface do seu código, mas isso é outra questão.

Exemplo: digamos que você tenha um valor representando o preço por unidade de uma mercadoria. O preço é expresso em centavos e, como você não negocia em centavos fracionários, decidiu tornar a propriedade um número inteiro (usarei C aqui porque não estou muito familiarizado com C #):

int _price

int pricePerUnit(void) {
    return _price;
}

int priceForUnits(int units) {
    return units * _price;
}

Tudo funciona bem até o dia em que alguém percebe que sua empresa está perdendo muito dinheiro devido a erros de arredondamento. Muitas das mercadorias que você rastreia são compradas e vendidas em muitos milhares de unidades; portanto, você precisa começar a acompanhar o preço com uma precisão de pelo menos 0,001 centavo. Como você foi inteligente o suficiente para encapsular o preço em vez de permitir que os clientes acessem diretamente, você pode fazer essa alteração rapidamente:

double _dprice

int pricePerUnit(void) {
    return (int)_dprice;
}

int priceForUnits(int units) {
    return (int)(units * _dprice);
}

A interface que os clientes usam para obter preços permanece a mesma, mas os dados recuperados agora são mais precisos. Se o preço por unidade for $ 1,001, priceForUnits(1000000)agora retornará um preço $ 1000 maior que antes. Isso acontece mesmo que você não tenha alterado a interface do sistema e, portanto, não violou nenhum código de cliente.

Agora, isso nem sempre pode ser tudo o que você precisa fazer. Às vezes, você precisará alterar ou aumentar sua interface para poder informar também o preço com mais precisão aos clientes:

double pricePerUnit() {
    return _dprice;
}

Uma mudança como essa vai quebrar o código do cliente, de modo que você pode, em vez manter a interface antiga e proporcionar uma melhor rotina mais recente,:

int pricePerUnit() {
    return (int)_dprice;
}

double accuratePricePerUnit() {
    return _dprice;
}

Você e o restante da sua equipe podem iniciar o processo de conversão de todos os clientes do seu sistema para usar o mais novo e melhor accuratePricePerUnit(). O código do cliente ficará mais preciso à medida que você progredir nessa tarefa, mas mesmo os itens antigos devem continuar funcionando tão bem quanto antes.

De qualquer forma, o ponto é que o encapsulamento permite alterar a maneira como os internos funcionam enquanto apresenta uma interface consistente e ajuda a fazer alterações úteis sem quebrar outro código. Nem sempre protege você de atualizar outro código, mas pode pelo menos ajudá-lo a fazer isso de maneira controlada.

Caleb
fonte
3

Na minha experiência, o encapsulamento torna muito mais difícil fazer a coisa "errada". Você pode agrupar a funcionalidade que semanticamente se une e isolá-la da funcionalidade que pode levar a um comportamento ruim ou imprevisível. Também pode ajudar a ocultar os detalhes do usuário final, o que pode ajudar a aumentar a segurança e a confiabilidade.

Considere este post de John D Cook . Considere que você tem um Breadobjeto. Uma coisa natural para cortar este pão. Então você escreve uma slice()função para poder fazer

slice(loaf)

com um novo loafobjeto que você criou. Isso faz sentido. Mas se você não tomar cuidado, você pode ligar acidentalmente

slice(finger)

com um fingerobjeto em algum lugar do seu projeto. Isso pode levar a coisas muito ruins. Em vez disso, encapsule essa função / método em uma Breadclasse para que você possa fazer isso

loaf.slice()

Isso definitivamente ajuda a evitar chamadas finger.slice()acidentalmente, pois fingerprovavelmente não possui um slice()método associado.

Este é um exemplo artificial, mas achei útil. Às vezes, o encapsulamento pode ser um aspecto subestimado do POO, mas é bom.

joshin4colours
fonte
2

O encapsulamento ajuda grandes grupos de desenvolvedores a coordenar seu trabalho com mais eficiência. Cada grupo de desenvolvedores trabalha em módulos diferentes, e esses módulos são encapsulados - separam o código em um pequeno número de operadores amplamente disponíveis cujo uso eles não podem controlar firmemente, e em um grande número de operadores internos que eles podem controlar firmemente. O que isso significa é que cada grupo de desenvolvedores pode definir invariantes que são importantes para o módulo manter, e garantir que esses invariantes sejam mantidos, independentemente do que os desenvolvedores de outros módulos façam.

Como o encapsulamento permite a preservação invariável, é freqüentemente usado para manter invariantes de segurança / proteção, por exemplo

  • que um armazenamento de dados nunca é acessado sem credenciais apropriadas
  • que uma conta bancária nunca tem mais dinheiro adicionado do que foi removido de outra
  • que certas operações são sempre registradas

O modelo de capacidade de objeto é uma metodologia para escrever código confidencial de segurança que é encapsulamento em esteróides.

O modelo de segurança conta com a impossibilidade de forjar referências; consulte Sintetizando endereços de atores.

Os objetos podem interagir apenas enviando mensagens nas referências. Uma referência pode ser obtida por:

  1. condições iniciais: No estado inicial do mundo computacional descrito, o objeto A já pode ter uma referência ao objeto B. paternidade: Se A cria B, naquele momento A obtém a única referência ao recém-criado B.
  2. doação: Se A cria B, B nasce com o subconjunto de referências de A com o qual A escolheu dotar.
  3. introdução: se A tiver referências a B e C, A poderá enviar a B uma mensagem contendo uma referência a C. B poderá reter essa referência para uso posterior.

No modelo de capacidade de objeto, todo o cálculo é realizado seguindo as regras acima.

As vantagens que motivam a programação orientada a objetos, como encapsulamento ou ocultação de informações, modularidade e separação de interesses, correspondem a objetivos de segurança como menos privilégios e separação de privilégios na programação baseada em recursos.

Mike Samuel
fonte
2

Como o encapsulamento ajudará ao fazer alterações no código e a partir de seus efeitos ondulantes.

Deixe-me dar um exemplo típico e simplista. Primeiro, suponha que você não esteja usando o encapsulamento: você tem um conjunto de dados e usa uma matriz para armazenar esses dados, e há outra parte do seu programa que usa essa matriz. Agora, se em algum momento você decidir que uma lista vinculada é a melhor opção para armazenar seus dados. Se você substituir a matriz pela lista vinculada, o que acontecerá? Seu programa será interrompido, a menos que você faça alterações em todo o lugar para substituir a lógica de processamento da matriz pela lógica de processamento da lista vinculada.

Mas, se você usa OO / Encapsulation, provavelmente particiona seu programa em classes, uma que armazena os dados e outra que usa os dados. Na primeira classe, você oculta sua implementação (encapsulamento) e expõe seus serviços através de métodos como

size()
remove(int index)
add(int index, Object o)
get(int index)

...

Nesse segundo caso, se você alterar a implementação da classe de armazenamento de array para lista vinculada ou para qualquer outra coisa, isso não afetará seus clientes. Sem efeito cascata.

Nazar Merza
fonte
1

A principal maneira que o encapsulamento ajuda a reduzir os efeitos ondulantes das mudanças é manter o máximo de detalhes da implementação em sigilo para a classe. Limitando a interface apenas aos membros necessários para usar a classe, muitas alterações podem ser feitas na implementação sem afetar nenhum código que use a classe.

Esse parece ser o argumento do texto que você citou, embora eu também tenha achado um pouco confuso na minha primeira leitura.

Jonathan Wood
fonte
1

Eu acho que isso permite que você altere a funcionalidade da sua classe sem, embora nem sempre, a introdução de quebras de BC (compatibilidade binária ou comportamental). Ou seja, você pode alterar a maneira como sua classe faz 'alguma coisa' sem precisar alterar a maneira como o usuário final diz para fazer essa 'alguma coisa'.

O exemplo que você deu para alterar o tipo de retorno é uma quebra de BC, porque qualquer pessoa que usou sua classe anteriormente não poderá usar a nova versão sem recompilar. Isso é algo que deve ser feito apenas como último recurso, e em um ambiente profissional só pode ser feito com autorização dos arquitetos e após a documentação da documentação, os clientes notificados etc.

Também oferece controle completo sobre o estado do seu objeto.

James
fonte