A sobrecarga é um exemplo do princípio Aberto / Fechado?

12

Wikipedia diz

"entidades de software (classes, módulos, funções, etc.) devem estar abertas para extensão, mas fechadas para modificação"

A palavra funções chamou minha atenção, e agora me pergunto se podemos assumir que criar uma sobrecarga para um método pode ser considerado como um exemplo do princípio Aberto / Fechado ou não?

Deixe-me explicar um exemplo. Considere que você possui um método em sua camada de serviço, usado em quase 1000 locais. O método obtém userId e determina se o usuário é administrador ou não:

bool IsAdmin(userId)

Agora considere que em algum lugar é necessário determinar se o usuário é administrador ou não, com base no nome de usuário, não userId. Se mudarmos a assinatura do método mencionado acima, quebramos o código em 1000 lugares (as funções devem ser fechadas para modificação). Assim, podemos criar uma sobrecarga para obter o nome de usuário, encontrar o userId com base no nome de usuário e no método original:

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

Dessa forma, estendemos nossa função criando uma sobrecarga para ela (as funções devem estar abertas para extensão).

É um exemplo de princípio aberto / fechado?

Saeed Neamati
fonte

Respostas:

5

Pessoalmente, interpretaria a declaração da wiki assim:

  • para uma classe: fique à vontade para herdar a classe e substituir ou estender sua funcionalidade, mas não invadir a classe original, alterando o que ela faz.
  • para um módulo (talvez uma biblioteca, por exemplo): sinta-se à vontade para escrever um novo módulo / biblioteca que agrupe o original, mescle funções em versões mais fáceis de usar ou amplie o original com recursos extras, mas não altere o módulo original.
  • para uma função (isto é, uma função estática, não um método de classe): seu exemplo é razoável para mim; reutilize a função IsAdmin (int) original dentro do novo IsAdmin (string). o original não muda, a nova função amplia sua funcionalidade.

a chance no pé, porém, é que, se o seu código estiver usando a classe 'cUserInfo' na qual IsAdmin (int) reside, você estará quebrando a regra e mudando a classe. infelizmente, a regra só seria mantida se você fizesse uma nova classe cUserWithNameInfo: public cUserInfo e coloque sua substituição IsAdmin (string) lá. Se eu possuísse a base de código, nunca obedeceria à regra. Eu diria besteira e apenas faça a alteração que você sugere.

Sassafras_wot
fonte
3

Primeiro, infelizmente, seu exemplo é artificial. Você nunca faria isso no mundo real, porque causa um duplo ganho desnecessário. Ou, pior, porque userid e username podem se tornar do mesmo tipo em algum momento. Ao invés de

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUser(userid);
    return user.IsAdmin();
}

public bool IsAdmin(string username)
{
    int userId = UserManager.GetUser(username).Id;
    return IsAdmin(userId);
}

Você realmente deveria estender essa classe.

public bool IsAdmin(int userid)
{
    User user = UserManager.GetUserById(userid);
    return user.IsAdmin();
}

public bool IsUsernameAdmin(string username)
{
    User userId = UserManager.GetUserByName(username);
    return user.IsAdmin();
}

Você pode refatorar e alterar ainda mais o primeiro nome do método para IsUserIdAdmin, para obter consistência, mas não é necessário. O ponto é que, ao adicionar o novo método e garantir que nenhum código de chamada seja quebrado, você descobriu que sua classe é extensível. Ou, em outras palavras, aberto à extensão .

E aqui está o princípio do aberto-fechado. Você nunca sabe realmente como seu código está em conformidade até tentar estender parte dele e ter que modificar (com o risco aumentado que vem com isso). Mas, com a experiência, você aprende a prever.

Como o tio Bob diz (grifo meu):

Como o fechamento não pode ser completo, deve ser estratégico. Ou seja, o designer deve escolher os tipos de alterações com as quais fechar seu design. Isso requer uma certa presciência derivada da experiência . O designer experiente conhece os usuários e a indústria suficientemente bem para julgar a probabilidade de diferentes tipos de mudanças. Ele então garante que o princípio aberto-fechado seja invocado para as mudanças mais prováveis.

pdr
fonte
Obrigado pela sua boa explicação. Mas ter mil viagens de ida e volta ou uma ida e volta não é o ponto principal aqui. Portanto, vamos esquecer o desempenho no momento. No entanto, o que fiz foi também estender a classe, porque adicionei outro método a ela, embora com o mesmo nome, mas com parâmetros de entrada diferentes. Mas gostei muito da sua citação do tio Bob . +1. ;)
Saeed Neamati
@SaeedNeamati: O que eu estava argumentando é que não importa se você está adicionando um método que usa um método existente ou adicionando um método que é totalmente independente, de qualquer forma que sua classe esteja aberta à extensão. Mas, se você tiver que modificar a interface do método existente para conseguir isso, não estará fechado para modificações.
pdr
2

Módulos que estão em conformidade com o princípio aberto-fechado têm dois atributos principais.

  1. Eles são "abertos para extensão". Isso significa que o comportamento do módulo pode ser estendido. Que podemos fazer com que o módulo se comporte de maneiras novas e diferentes conforme os requisitos do aplicativo mudam, ou para atender às necessidades de novos aplicativos.
  2. Eles estão "Fechados para modificação". O código fonte desse módulo é inviolável. Ninguém tem permissão para fazer alterações no código fonte.
  1. Ao sobrecarregar seu método, você está estendendo a funcionalidade do módulo existente, atendendo assim às novas necessidades do seu aplicativo

  2. Você não está fazendo alterações em um método existente, portanto não está violando a segunda regra.

Gostaria de ouvir o que os outros têm a dizer sobre não permitir alterar o método original. Sim, você pode colocar modificadores de acesso apropriados e aplicar o encapsulamento, mas o que mais pode ser feito?

Ref: http://www.objectmentor.com/resources/articles/ocp.pdf

CodeART
fonte
1

Princípio Aberto-Fechado é uma meta, um caso ideal, nem sempre uma realidade. Particularmente ao procurar refatorar códigos antigos, o primeiro passo é muitas vezes uma modificação pesada para tornar o OCP uma possibilidade. A raiz do princípio é que o código de trabalho já funciona e a alteração possivelmente introduz bugs. Portanto, o melhor cenário é não alterar o código existente, apenas adicionar um novo código.

Mas digamos que você tenha uma função chamada BigContrivedMethod(int1, int2, string1). BigContrivedMethodfaz três coisas: coisa1, coisa2 e coisa3. Neste ponto, a reutilização do BCM provavelmente é difícil, porque faz muito. A refatoração (se possível) em ContrivedFunction1(int), ContrivedFunction2(int)e ContrivedFunction3(string)fornece três métodos menores e mais focados que você pode combinar mais facilmente.

E essa é a chave do OCP em relação a métodos / funções: composição. Você "estende" funções chamando-as de outras funções.

Lembre-se de que o OCP faz parte de outros 5 princípios, as diretrizes do SOLID. Essa primeira é a chave, a responsabilidade única. Se tudo na sua base de código fizesse apenas a coisa específica a ser executada, você nunca precisaria modificar o código. Você só precisará adicionar um novo código ou combinar o código antigo de novas maneiras. Como o código real raramente atende a essa diretriz, você geralmente precisa modificá-lo para obter o SRP antes de obter o OCP.

CodexArcanum
fonte
Acho que você fala sobre o Responsável Único aqui, não sobre OCP.
Saeed Neamati
1
Estou dizendo que você não pode ter OCP realisticamente sem o SRP. Código que faz muito não pode ser estendido, não pode ser reutilizado. O SOLID é quase escrito na ordem de importância, com cada princípio confiando nos que antes eram viáveis.
CodexArcanum
Esta resposta deve ser votada mais.
Ashish Gupta
0

Você está seguindo o princípio de aberto-fechado se estiver alterando o comportamento do seu programa escrevendo novo código em vez de alterar o código antigo.

Como é praticamente impossível escrever código aberto a todas as alterações possíveis (e você não deseja porque entra em paralisia na análise), escreve um código que responde a todos os diferentes comportamentos nos quais você está trabalhando atualmente por extensão, em vez de modificação.

Quando você encontra algo que precisa mudar, em vez de simplesmente alterar algo para permitir o novo comportamento, descobre qual é o ponto da mudança, reestrutura o programa sem alterar o comportamento, para que possa mudar esse comportamento escrevendo novo código .

Então, como isso se aplica ao seu caso:

Se você estiver adicionando novas funções à sua classe, ela não será aberta / fechada, mas os clientes da função que você está sobrecarregando.

Se você está simplesmente adicionando novas funções que funcionam em sua classe, então ele e os clientes da sua função estão abertos / fechados.

Edward Strange
fonte