OOP, a "Santíssima Trindade" e o SOLID: algum conhecimento mínimo sobre eles

Entrada obrigatória


Não posso garantir que as interpretações dos termos e princípios geralmente aceitos estabelecidos aqui coincidam com o que os professores da Califórnia apresentaram em sólidos artigos científicos na segunda metade do século passado. Não posso garantir que minhas interpretações sejam totalmente compartilhadas ou compartilhadas pela maioria dos profissionais de TI do setor ou da comunidade acadêmica. Não posso nem garantir que minhas interpretações o ajudarão na entrevista, embora eu assuma que elas serão úteis.


Mas garanto que, se a falta de entendimento for substituída por minhas interpretações e começar a aplicá-las, o código que você escreveu será mais fácil de manter e modificar. Também entendo perfeitamente que nos comentários vou escrever furiosamente complementar o que foi escrito, o que tornará possível corrigir omissões e inconsistências absolutamente flagrantes.


Essas pequenas garantias levantam questões sobre os motivos pelos quais o artigo foi escrito. Acredito que essas coisas devem ser ensinadas onde quer que ensinem programação, até aulas de ciências da computação em escolas com um estudo aprofundado. No entanto, para mim, tornou-se uma situação assustadoramente normal quando descobri que o interlocutor é meu colega e que trabalha há vários anos, mas com o encapsulamento “ouvi algo lá”. A necessidade de coletar tudo isso em um só lugar e fornecer um link quando surgirem dúvidas permanece madura por um longo tempo. E então meu "projeto de estimação" me deu uma boa quantidade de alimento para pensar.


Aqui eles podem me opor que é muito cedo para aprender essas coisas na escola e, em geral, a luz ainda não se juntou no POO. Em primeiro lugar, depende de como você aprende. Em segundo lugar, 70% do material deste artigo se aplica não apenas ao POO. O que vou observar separadamente.




OOP em poucas palavras


Esta é provavelmente a seção mais difícil para mim. Ainda assim, você precisa estabelecer a base e descrever muito brevemente qual é a essência da OOP para entender por que o encapsulamento, o polimorfismo, a herança e os princípios do SOLID a fortaleceram. E farei isso falando sobre como você pode pensar em algo assim.


Começou com Dijkstra, que provou que qualquer algoritmo pode ser expresso de três maneiras para selecionar o seguinte comando: execução linear (em ordem), ramificação por condição, execução de loop enquanto a condição é atendida. Usando essas três conexões, você pode construir qualquer algoritmo. Além disso, foi recomendado escrever programas, limitando-se ao arranjo linear de comandos um após o outro, ramificação e loops. Isso foi chamado de "programação estrutural processual " (obrigado pelo esclarecimento sshikov ).


Também aqui observamos que a sequência de comandos deve ser combinada em subprogramas e cada subprograma pode ser executado usando um comando.


Além da ordem das ações, é importante para nós quais ações são executadas. E eles são executados em dados que se tornaram habituais armazenar em variáveis. Variáveis ​​armazenam dados. Eles são interpretados com base em seu tipo. Obviamente, para o ranger de dentes, mas seja um pouco paciente, por favor.


Desde o início, um conjunto mais ou menos geral de tipos de dados primitivos foi formado. Inteiros, números reais, variáveis ​​booleanas, matrizes, strings. Algoritmos + estruturas de dados = programas, como legado por Nicklaus Wirth.


Além disso, desde o início dos tempos, havia sob diferentes formas um tipo de dados como um subprograma. Ou um pedaço de código, se quiser. Alguns podem dizer que o uso de subprogramas como variáveis ​​é uma prerrogativa da programação funcional. Mesmo assim, a capacidade de criar um código de variável é uniforme no assembler. Deixe essa possibilidade ser reduzida para "bem, aqui está o número de bytes na RAM onde esta sub-rotina mora e, em seguida, o comando CALL com a pilha de chamadas nos dentes e girar o máximo possível".


Certamente, vários tipos de dados eram poucos e as pessoas começaram a pensar em adicionar a capacidade de criar seus próprios tipos a vários PLs. Uma variação desse recurso foram as chamadas gravações. Abaixo estão dois exemplos de gravação em uma linguagem de programação inexistente (a seguir, NEPL - Linguagem de Programação Não Existente):


type Name: record consisting of FirstName: String, MiddleName: String, LastName: String. type Point: record consisting of X: Double, Y: Double. 

Ou seja, em vez de arrastar duas ou três variáveis ​​relacionadas, você as agrupa em uma estrutura com os campos nomeados. Em seguida, você declara uma variável do tipo Nome e se refere ao campo Nome, por exemplo.


O que é tão valioso nessa variável "aprimorada" para o nosso tópico? Que daqui existe apenas um pequeno passo para o POO. Não apenas destaquei um parágrafo em negrito para indicar que partes do código também podem ser colocadas em variáveis. Veja como as variáveis ​​se transformam em objetos:


 type Name: class consisting of FirstName: String, MiddleName: String, LastName: String, GetFullName: subprogram with no parameters returns String. type Point: class consisting of X: Double, Y: Double, ScalarMultiply: subprogram with (Double) parameters returns Point. 

NB NEPL está desenvolvendo ativamente e já substituiu a palavra-chave record por classe.


Ou seja, podemos acessar o campo "GetFullName" e chamá-lo . Uma variável contém não apenas dados que descrevem seu estado, mas também comportamento. Assim, a variável se transforma em um objeto que possui algumas habilidades e estado. E já estamos trabalhando não apenas com variáveis, mas com pequenos sistemas que podem receber comandos.


Na minha juventude, essa ideia me fascinou. Basta pensar, você pode criar qualquer tipo de dados . E você não está trabalhando com alguns números, mas com os objetos do mundo que está criando. Nenhum tormento com matrizes chatas ou conjuntos complexos de números. Trabalhamos diretamente com objetos dos tipos Jogador, Inimigo, Bala, Chefe! Sim, na minha juventude, eu queria fazer videogames.


Na realidade, tudo acabou não sendo tão simples. E sem algumas idéias "reforçadoras", a OOP transformará a vida de um programador em um inferno. Mas antes de prosseguir, vamos dar mais alguns termos:


  • Os tipos de dados que são "reforçados" por seu comportamento no OOP são chamados de classes .
  • Variáveis ​​desses tipos são chamadas de objetos .
  • E as rotinas que definem o comportamento dos objetos são chamadas de métodos . Como regra, cada classe tem seu próprio conjunto de métodos, e não cada objeto. Para que cada objeto de uma determinada classe se comporte como outros objetos da mesma classe. Ficarei feliz em saber dos comentários sobre idiomas em que as coisas são diferentes.

Santíssima Trindade


Aconteceu historicamente que eles perguntam sobre essas coisas em entrevistas. Eles são escritos em qualquer livro didático de linguagem OOP. Porque Porque se você projetar um programa de POO sem considerar o encapsulamento e o polimorfismo, receberá um "caixão, caixão, cemitério, desacompanhado". A herança não é tão estritamente necessária, mas esse conceito permite que você entenda melhor o POO e é uma das principais ferramentas no design usando o POO.


Encapsulamento


Bem, vamos começar com a definição da Wikipedia: "empacotando dados e funções em um único componente". A definição parece clara, mas ao mesmo tempo generalizada demais. Portanto, vamos falar sobre por que isso é necessário, o que isso nos dará e exatamente como compactar dados e funções em um único componente.


Eu já escrevi um artigo que tratava de encapsulamento. E lá fui justamente criticado pelo fato de ter reduzido o encapsulamento à ocultação de informações, e essas são coisas um pouco diferentes. Em particular, o EngineerSpock produziu uma formulação tão elegante como proteção invariável . Admito o meu erro e depois vou explicar por que o cometi.


Enquanto isso, a minha é uma definição preliminar do princípio do encapsulamento, ou, se você quiser, o processo de encapsulamento, que descreve não apenas o princípio do encapsulamento, mas também o que deve ser alcançado com ele:


Qualquer entidade de software com um estado não trivial deve ser transformada em um sistema fechado, que só pode ser transferido de um estado correto para outro.


Sobre a parte em que "qualquer entidade de software que tenha um estado não trivial", vamos um pouco mais tarde. Por enquanto, estaremos falando exclusivamente sobre objetos. E sobre a segunda parte da minha definição. Por que precisamos disso?


Tudo é simples aqui: o que só pode ser transferido de um estado correto para outro não pode ser quebrado. Ou seja, precisamos garantir que nenhum objeto possa ser quebrado. Parece, para dizer o mínimo, ambicioso. Como conseguir isso?


No zero, tudo o que se relaciona ao objeto deve estar em um só lugar, dentro da mesma borda arquitetônica, digamos. Caso tenha ficado muito obscuro, repetirei a definição da Wikipedia: "empacotando dados e funções em um único componente".


Primeiro, para separar claramente a interface e sua implementação. Acho que todos os meus colegas estão familiarizados com a API de abreviação. Portanto, cada objeto deve ter sua própria API, ou PI, se quisermos ser meticulosos. Aquilo para o qual eles criam, e aquilo que os outros irão usar, o que eles causarão. Como deveria ser? Para que ninguém sequer pensasse em entrar em um objeto e usá-lo de forma inadequada. Mas não mais do que isso.


Em um livro, infelizmente, não me lembro qual, isso foi explicado pelo exemplo de um microondas. Existem botões nele. Canetas Eles permitem que você aqueça os alimentos. Você não precisa relaxar o microondas e soldar algo lá para aquecer a sopa de ontem. Você tem uma interface, botões. Coloque um prato, pressione alguns botões, espere um minuto e seja feliz.


Basta pensar em quais botões o usuário do seu objeto precisa pressionar e separá-los dos gibets internos. E, em nenhum caso, não adicione botões extras! Essa foi a primeira.


Segundo, respeite o limite entre a interface e a implementação e faça com que outros o respeitem. Em princípio, essa idéia é intuitiva e paira entre a sabedoria popular de várias formas. Tome pelo menos "se você se aproveitou de algo não documentado e depois algo quebrou para você, você é o culpado". Eu acho que, com "não gire o microondas até que funcione da maneira que você precisa", tudo fica claro. Agora, sobre como fazer os outros respeitarem a fronteira notória.


É aqui que a própria ocultação de informações vem em socorro. Sim, você sempre pode concordar, perguntar, configurar convenções de código, indicar uma revisão de código que isso não é possível. Mas a própria possibilidade de subir além dessa fronteira permanecerá. Esta é a própria ocultação de informações que vêm em socorro.


Não podemos atravessar a fronteira notória se nosso código não puder aprender sobre sua existência e o que está por trás dela. E mesmo que ele descubra, o compilador fingirá que não existe tal campo ou método, e mesmo que exista, não é necessário tocá-lo, eu me recuso a compilar e, geralmente, quem você é, não o chamamos, usa a parte da interface.



É aqui que todos os tipos de modificadores de acesso públicos, privados e outros entram em jogo a partir do seu idioma favorito. A própria "ocultação de informações" é a maneira mais confiável de manter os benefícios do encapsulamento no ralo. De qualquer forma, não faz sentido agrupar tudo o que diz respeito a uma classe em um só lugar, se o código usar o que deseja e onde deseja. Mas, com a ocultação de informações, tal situação não deveria mais surgir em princípio. E esse método é tão confiável que, na mente de milhares e milhares de programadores (inclusive eu, que já está lá), a diferença entre encapsulamento e ocultação de informações é, de alguma forma, suavizada.


E se o seu YP favorito não permitir que você oculte informações? Neste tópico, você pode se divertir falando sobre comentários. Eu vejo a próxima saída. Crescente:


  • Documente apenas a parte da interface e considere tudo o que não está documentado como uma implementação.
  • Separe a implementação da interface via convenção de código (exemplo - em python, existe a variável __all__ que indica o que exatamente será importado do módulo quando você solicitar a importação de tudo).
  • Torne essas convenções de código muito estritas para que possam ser verificadas automaticamente, após o que qualquer violação delas será equiparada a um erro de compilação e uma compilação caída.

Mais uma vez:


  • Tudo relacionado a uma classe é compactado em um módulo.
  • Entre as classes, limites estritos da arquitetura são traçados.
  • Em qualquer classe, a parte da interface é separada da implementação desta parte da interface.
  • Os limites entre as classes devem ser respeitados e obrigados a respeitar os outros!

Termino com um exemplo no NEPL, que ainda está em desenvolvimento muito ativo e, após dez parágrafos, adquiriu modificadores de acesso:


 type Microwave: class consisting of private fancyInnerChips: List of Chip, private foodWarmingThing: FoodWarmerController, private buttonsPanel: ButtonsPanel, public GetAccessToControlPanel: subprogram with no parameters returns ButtonsPanel, public OpenDoor: subprogram with no parameters returns nothing, public Put: subprogram with (Food) parameters return nothing, public CloseDoor: subprogram with no parameters returns nothing. type ButtonsPanel: class consisting of private buttons: List of ButtonState, public PressOn: subprogram with no parameters returns nothing, public PressOff: subprogram with no parameters returns nothing, public PressIncreaseTime: subprogram with no parameters returns nothing, public PressDecreaseTime: subprogram with no parameters returns nothing, public PressStart: subprogram with no parameters returns nothing, public PressStop: subprogram with no parameters returns nothing. 

Espero que fique claro no código qual é a essência do exemplo. Esclareço apenas um ponto: GetAccessToControlPanel verifica se podemos tocar no micro-ondas. E se ela estiver quebrada? Então você não pode clicar em nada. Você só pode receber uma mensagem de erro.


Bem, o fato de o ButtonsPanel se tornar uma classe separada sem problemas nos leva a uma pergunta importante: qual é o "componente único" da definição de encapsulamento na Wikipedia? Onde e como devem estar os limites entre as classes? Definitivamente voltaremos a esse problema um pouco mais tarde.

Spoiler
Princípio da responsabilidade única

Use fora do OOP


Muitos programadores aprenderam sobre encapsulamento em um tutorial em Java / C ++ / C # / substituem sua primeira linguagem OOP. Portanto, o encapsulamento na consciência de massa, de alguma forma, está ligado ao POO. Mas vamos voltar às duas definições de encapsulamento.


Empacotando dados e funções em um único componente.

Qualquer entidade de software com um estado não trivial deve ser transformada em um sistema fechado, que só pode ser transferido de um estado correto para outro.


Você já reparou? Não há nada sobre classes e objetos!


Então, um exemplo. Você é um DBA . Seu trabalho é ficar de olho em algum tipo de banco de dados relacional. Que seja, por exemplo, no MySQL. Seu precioso banco de dados usa vários programas. Você não tem controle sobre alguns deles. O que fazer


Crie um conjunto de procedimentos armazenados. Nós os compomos em um circuito, que chamaremos de interface. Criamos um usuário para nossos programas sem nenhum direito. Este é o comando CREATE USER. Em seguida, usando o comando GRANT, concede aos usuários apenas o direito de executar esses procedimentos armazenados a partir do esquema da interface.


Só isso. Temos um banco de dados, a própria entidade de software com um estado não trivial, que é fácil de quebrar. E para não quebrá-lo, criamos uma interface a partir de procedimentos armazenados. E após os meios do próprio MySQL, fazemos isso para que entidades de terceiros possam usar apenas essa interface.


Observe que o notório encapsulamento, como é, e como foi descrito, é usado em todo o seu potencial. Mas existe uma lacuna entre a representação relacional de dados e objetos que precisa ser fechada com estruturas ORM volumosas.


É por isso que classes e objetos não aparecem na definição de encapsulamento. A ideia é muito mais ampla que o POO. E traz muito benefício falar somente em livros didáticos sobre idiomas OOP.


Polimorfismo


O polimorfismo tem muitas formas e definições. Basta que Kondratius me basta quando abro a Wikipedia. Aqui vou falar sobre polimorfismo, como a Straustrup o formulou: uma interface - muitas implementações .


Nesta forma, a idéia de polimorfismo pode fortalecer muito a posição dos escritores com o olho no encapsulamento. Afinal, se separamos a interface da implementação, quem usa a interface não precisa saber que algo mudou na implementação. Quem usa a interface (idealmente) nem precisa saber que a implementação mudou! E isso abre infinitas possibilidades de expansão e modificação. Seu antecessor decidiu que é melhor aquecer a comida com um radar militar? Se esse gênio louco separou a interface da implementação e a formalizou claramente, então o radar militar pode ser adaptado a outras necessidades, e sua interface para aquecimento de alimentos pode ser realizada usando um microondas.


A NEPL está se desenvolvendo rapidamente e, sob a influência do C #, adquire (com cuidado, não tropeça nas palavras) o tipo de interface de dados.


 type FoodWarmer: interface consisting of GetAccessToControlPanel: no parameters returns FoodWarmerControlPanel, OpenDoor: no parameters returns nothing, Put: have (Food) parameters returns nothing, CloseDoor: no parameters returns nothing. type FoodWarmerControlPanel: interface consisting of PressOn: no parameters returns nothing, PressOff: no parameters returns nothing, PressIncreaseTime: no parameters returns nothing, PressDecreaseTime: no parameters returns nothing, PressStart: no parameters returns nothing, PressStop: no parameters returns nothing. type EnemyFinder: interface consisting of FindEnemies: no parameters returns List of Enemy. type Radar: class implementing FoodWarmer, EnemyFinder and consisting of private secretMilitaryChips: List of Chip, private giantMicrowavesGenerator: FoodWarmerController, private strangeControlPanel: AlarmClock, public GetAccessToControlPanel: subprogram with no parameters returns FoodWarmerControlPanel, public OpenDoor: subprogram with no parameters returns nothing, public Put: subprogram with (Food) parameters return nothing, public CloseDoor: subprogram with no parameters returns nothing, public FindEnemies: subprogram with no parameters returns List of Enemy. type AlarmClock: class implementing FoodWarmerControlPanel and consisting of private mechanics: List of MechanicPart, public PressOn: subprogram with no parameters returns nothing, public PressOff: subprogram with no parameters returns nothing, public PressIncreaseTime: subprogram with no parameters returns nothing, public PressDecreaseTime: subprogram with no parameters returns nothing, public PressStart: subprogram with no parameters returns nothing, public PressStop: subprogram with no parameters returns nothing. type Microwave: class implementing FoodWarmer and consisting of private fancyInnerChips: List of Chip, private foodWarmingThing: FoodWarmerController, private buttonsPanel: ButtonsPanel, public GetAccessToControlPanel: subprogram with no parameters returns FoodWarmerControlPanel, public OpenDoor: subprogram with no parameters returns nothing, public Put: subprogram with (Food) parameters return nothing, public CloseDoor: subprogram with no parameters returns nothing. type ButtonsPanel: class implementing FoodWarmerControlPanel and consisting of private buttons: List of ButtonState, public PressOn: subprogram with no parameters returns nothing, public PressOff: subprogram with no parameters returns nothing, public PressIncreaseTime: subprogram with no parameters returns nothing, public PressDecreaseTime: subprogram with no parameters returns nothing, public PressStart: subprogram with no parameters returns nothing, public PressStop: subprogram with no parameters returns nothing. 

Portanto, se uma classe é declarada como implementando uma interface, ela deve implementar todos os métodos dessa interface. Caso contrário, o compilador nos dirá "fi". E temos duas interfaces: FoodWarmer e FoodWarmerControlPanel. Olhe para eles com cuidado e depois vamos analisar as implementações.


Como legado do difícil passado soviético, recebemos a classe Radar de uso duplo, que pode ser usada para aquecer alimentos e encontrar o inimigo. E, em vez do painel de controle, um alarme é usado, porque o plano foi excedido e eles precisam ser colocados em algum lugar. Felizmente, porém, o MNS sem nome do Instituto de Pesquisa em Química, Fertilizantes e Venenos, para o qual eles utilizaram, implementou as interfaces FoodWarmer para o radar e o FoodWarmerControlPanel para o despertador.


Depois de uma geração, ocorreu a alguém que é melhor aquecer a comida com um microondas, e é melhor controlar o microondas com os botões. E agora as classes Microwave e ButtonsPanel criadas. E eles implementam as mesmas interfaces. FoodWarmer e FoodWarmerControl. O que isso nos dá?


Se em todo lugar em nosso código usamos uma variável como FoodWarmer para aquecer alimentos, podemos simplesmente substituir a implementação por uma mais moderna e ninguém notará nada. Ou seja, o código que usa a interface não se importa com os detalhes da implementação. Ou ao fato de que mudou completamente. Podemos até criar a classe FoodWarmerFactory que produz diferentes implementações do FoodWarmer, dependendo da configuração do seu aplicativo.


Observe também os campos fechados na classe Microondas e Radar. Lá temos um despertador e um painel com botões. Mas, fora, damos uma variável do tipo FoodWarmerControlPanel.


Em algum lugar de Picabu, havia uma história sobre como um certo candidato explicou o princípio do polimorfismo da seguinte maneira:

Aqui eu tenho uma caneta. Posso escrever meu nome para ela, mas posso grudar nos seus olhos. Este é o princípio do polimorfismo.
A imagem é engraçada, a situação é terrível e a explicação do princípio do polimorfismo é inútil.

O princípio do polimorfismo não é que uma classe de caneta com algum medo perceba as interfaces de um papel de carta e aço frio ao mesmo tempo. O princípio do polimorfismo é que tudo o que pode ser picado pode ficar preso nos olhos. Porque pode ser picado. E o resultado será diferente, mas idealmente deve gerar privação visual. E o método do polimorfismo permite refletir esse fato no modelo que você está construindo para o seu mundo.


Use fora do OOP


Existe uma linguagem tão engraçada e engraçada em todos os sentidos dessas palavras como Erlang. E tem uma característica como comportamento. Assista suas mãos:


O código é dividido em módulos. Você pode usar o nome do módulo como uma variável. Ou seja, você pode escrever uma chamada de função de um módulo como este:


 %option 1 foobar:function(), %option 2 Module = foobar, Module:function(). 

Para garantir que o módulo tenha certas funções, existe um recurso da linguagem como comportamento. Em um módulo que usa outros módulos, você define os requisitos para os módulos variáveis ​​usando a declaração behaviour_info. E então os módulos que seu módulo pai, que especificou behaviour_info, usarão, usando a declaração de comportamento, informam ao compilador: "nos comprometemos a implementar esse comportamento para que o módulo pai possa nos usar".


Por exemplo, o módulo gen_server permite criar um servidor que de forma síncrona ou assíncrona em um processo separado (não há encadeamentos em Erlang, apenas milhares de pequenos processos paralelos), executa solicitações de outros processos. E em gen_server toda a lógica relacionada a solicitações de outros processos é coletada. Mas o processamento direto dessas solicitações é feito por quem implementa o comportamento de gen_server. E enquanto outros módulos o implementam corretamente (mesmo se houver stubs vazios), o gen_server geralmente não se importa como essas solicitações são processadas. Além disso, o módulo de processamento pode ser alterado rapidamente.


Uma interface - muitas implementações. Como Straustrup nos legou. Conforme escrito em livros inteligentes sobre OOP. E agora uma citação da wikipedia para o estúdio:


Erlang — , .

« — » , , .


. .NET. . CLR . CLR Microsoft , , , ( ECMA-335).


.NET , Windows, Windows Phone, XBox ( XNA, , ), . Microsoft. , , . , Mono Project. .NET. .


, . Microsoft , .NET . . , , .NET Core. , .NET Core .NET Framework, , , . , .


, « — » - . , .



, . , , , . , .


, , . , , NEPL. . Name? , . EtiquetteInfo - .


 import class EtiquetteInfo from Diplomacy. type PoliteName: class consisting of private FirstName: String, private MiddleName: String, private LastName: String, for descendants GetPoliteFirstName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteMiddleName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteLastName: subprogram with (EtiquetteInfo) parameters returns String, public GetFullName: subprogram with (EtiquetteInfo) parameters returns String. subprogram GetPoliteFirstName.PoliteName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return _EtiquetteInfo.PoliteFirstName(FirstName). subprogram GetPoliteMiddleName.PoliteName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return _EtiquetteInfo.PoliteMiddleName(MiddleName). subprogram GetPoliteLastName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return _EtiquetteInfo.PoliteLastName(LastName). subprogram GetFullName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as return GetPoliteFirstName(_EtiquetteInfo) + GetPoliteMiddleName(_EtiquetteInfo) + GetPoliteLastName(_EtiquetteInfo). 

, GetFullName - , ( , , ?). , , - . , , . , , , , . , . PoliteName . ExoticPoliteName — . , , .


- . ExoticPoliteName, PoliteName, . PoliteExoticName. , PoliteName.


 import class EtiquetteInfo from Diplomacy. type PoliteExoticName: class extending PoliteName and consisting of private MoreMiddleNames: List of String, for descendants overridden GetPoliteMiddleName: subprogram with (EtiquetteInfo) parameters returns String, public overriden GetFullName: subprogram with (EtiquetteInfo) parameters returns String. subprogram GetPoliteMiddleName.PoliteExoticName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as String AggregatedMiddleName = String.Join(" ", MoreMiddleNames), return base.GetPoliteMiddleName(_EtiquetteInfo + AggregatedMiddleName). subprogram GetFullName with (EtiquetteInfo _EtiquetteInfo) parameters returning String implemented as String Prefix = "", String FirstName = GetFirstName(_EtiquetteInfo), if _EtiquetteInfo.ComplimentIsAppropriate(FirstName) then Prefix = "Oh, joy of my heart, dear ", return Prefix + base.GetFullName(_EtiquetteInfo). 

: PoliteName . PoliteExoticName -.


, , . , GetPoliteFirstName GetPoliteLastName. . GetFullName, , .


, , PoliteName, PoliteExoticName, GetFullName. , PoliteName, , . , , base.GetFullName(etiquetteInfo). , , .


, " ". , . : , . . .


, . . , Boolean, , . , Object. . , , , , Object, .


, NEPL . PoliteName Object, PoliteExoticName PoliteName Object . , NEPL :


 subprogram Foo.Bar with no parameters returning nothing implemented as PoliteExoticName _PoliteExoticName = GetSomePoliteExoticName(), PoliteName _PoliteName = _PoliteExoticName, Object _Object = _PoliteExoticName. 

, , _Object.GetFullName, , . PoliteName PoliteExoticName - Object, - _Object, .


? , . . , ( Object) , - -.


, , , , . ? , . - , . . - , .


? , . . . , «» , . ?


. , NEPL for descendants.


 type PoliteName: class consisting of private FirstName: String, private MiddleName: String, private LastName: String, for descendants GetPoliteFirstName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteMiddleName: subprogram with (EtiquetteInfo) parameters returns String, for descendants GetPoliteLastName: subprogram with (EtiquetteInfo) parameters returns String, public GetFullName: subprogram with (EtiquetteInfo) parameters returns String. 

PoliteExoticName FirstName, «, , , , ». GetPoliteFirstName FirstName.



, , Square Shape, Shape Square . , . Shape , , . Square, Shape. Porque , Shape, .


. , ? -, . -, , , , . , , .


, . , . « »? , . . , .


. , . , , . . , . , , .


, , . . ( , ), , . , , . , , . .


, ) , ) , , , 999 1000 . , , .


()


, . - , . , , . - , .


SOLID


— , , . - . SOLID — , , … ? ? , , .


S — The Single Responsibility Principle


. .


.

. . .


, « » - . . « ?». , , ? .


, SRP :

.

, ? — , . ? . « ». , . ? .


, ? . , .txt-. , , , .txt-. - , . , , … .txt-. Porque . , , .txt-.


NB ( , ) «», « , ».



. , , , .txt-. . , . , . Mas! -, , , . -, , , .



, , . , ) , ) .html-. , , .txt/.html.


, , , . , , .txt-. O que poderia dar errado?


  • . , . , , . , , , , , , . .
  • , . 900 USD 900.00$? 20190826T130000 2019 ? , ?
  • .txt-? .csv? , .txt. ? ? - ? - ? , ?

? — . , -.


, , , . , .


DRY — Don't Repeat Yourself


:


, . .

, , , - , . , , .



SRP . , , . , « ». , , , — .


. HTML . , «» . , HTML 90- . , . - , . , HTML- . CSS .


? -, CSS , «» . , . -, CSS- html- , - text-color . — . .


O — The Open/Closed Principle


, , , - . , «» «». / , , . « » . :


.

. , . , . , — .


, . . ? . , .


  • , . , . , / .
  • , . , . / /, . NB /. , : .
  • , . , .
  • , - , , . (), () «» .
  • . , , ? , . , . , .

, . .



 type SpellChecker: class consisting of public DoSpellCheck: subprogram with (String) parameters returns String. type CorporativeStyleChecker: class consisting of public DoCorporativeStyleCheck: subprogram with (String) parameters returns String. type TextProcessor: class consisting of private Text: String, private SpellChecker: SpellChecker, private CorporativeStyleChecker: CorporativeStyleChecker, public Process: subprogram with no parameters returns String. subprogram TextProcessor.Process with no parameters returning String implemented as String ProcessedText = Text, ProcessedText = SpellChecker.DoSpellCheck(ProcessedText), ProcessedText = CorporativeStyleChecker.DoCorporativeStyleCheck(ProcessedText), return ProcessedText. 

,


 type TextChecker: interface consisting of Check: have (String) parameters returns String. type SpellChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type CorporativeStyleChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type TextProcessor: class consisting of private Text: String, private SpellChecker: SpellChecker, private CorporativeStyleChecker: CorporativeStyleChecker, public Process: subprogram with no parameters returns String. subprogram TextProcessor.Process with no parameters returning String implemented as String ProcessedText = Text, List of SpellChecker Checkers = (SpellChecker, CorporativeStyleChecker), for each SpellChecker SpellChecker in Checkers do ProcessedText = SpellChecker.Check(ProcessedText) and nothing else, return ProcessedText. 

O/CP . TextCheckersSupplier, .


 type TextChecker: interface consisting of Check: have (String) parameters returns String. type SpellChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type CorporativeStyleChecker: class implementing TextChecker and consisting of public Check: subprogram with (String) parameters returns String. type TextCheckersSupplier: class consisting of public GetCheckers: subprogram with no parameters returns List of TextChecker. type TextProcessor: class consisting of private Text: String, private CheckersSupplier: TextCheckersSupplier, public Process: subprogram with no parameters returns String. subprogram TextProcessor.Process with no parameters returning String implemented as String ProcessedText = Text, List of SpellChecker Checkers = CheckersSupplier.GetCheckers(), for each SpellChecker SpellChecker in Checkers do ProcessedText = SpellChecker.Check(ProcessedText) and nothing else, return ProcessedText. 

? , , , . , TextProcessor. , TextCheckerSupplier , , . TextChecker' . , , , . , .



, , , , . .


L — The Liskov Substitute Principle


, :


, , , .

, , . ?



- NEPL:


 type PoliteExoticName: class extending PoliteName and consisting of... subprogram Foo.Bar with no parameters returning nothing implemented as PoliteExoticName _PoliteExoticName = GetSomePoliteExoticName(), PoliteName _PoliteName = _PoliteExoticName, Object _Object = _PoliteExoticName. 

, _PoliteName - . , , . , . PoliteName, . , , , PoliteName . , , , . .


, -, allex ( , ). , , :

-, .

, «Agile Principles, Patterns and Practices in C#». NEPL, .


  Object _Object = GetObjectSomewhere(), PoliteExoticName IHopeItsActuallyName = _Object as PoliteExoticName, 

, , . . , . - , . , . . ( , alias , ):


 from UnboundedCollections import UnboundedSet as ThirdPartyUnboundedSet. from BoundedCollections import BoundedSet as ThirdPartBoundedSet. type Set: interface consisting of Add: have (Object) parameters returns nothing, Delete: have (Object) parameters returns nothing, IsMember: have (Object) parameters returns Boolean. type UnboundedSet: class implementing Set and consisting of private ThirdPartySet: ThirdPartyUnboundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. type BoundedSet: class implementing Set and consisting of private ThirdPartySet: ThirdPartyBoundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. subprogram BoundedSet.Add with (Object O) parameters returning nothing implemented as ThirdPartSet.Add(O). 


. . . PersistentSet, - . , PersistentObject. - . Delete IsMember . Add...


 from PersistentCollections import PersistentSet as ThirdPartyPersistentSet, PersistentObject. type PersistentSet: class implementing Set and consisting of private ThirdPartySet: ThirdPartyPersistentSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. subprogram PersistentSet.Add with (Object O) parameters returning nothing implemented as PersistentObject Po = O as PersistentObject, ThirdPartySet.Add(Po). 


. PersistentSet Object, . , , Set , . ( ):


 type MemberContainer: interface consisting of Delete: have (Object) parameters returns nothing, IsMember: have (Object) parameters returns Boolean. type Set: interface extending MemberContainer and consisting of Add: have (Object) parameters returns nothing. type PersistentSet: interface extending MemberContainer and consisting of Add: have (PersistingObject) parameters returns nothing. 


C#.


, NEPL

NEPL. List of String. , .


 type List: class generalized with (T) parameters consisting of 

Set PersistentSet.


 from UnboundedCollections import UnboundedSet as ThirdPartyUnboundedSet. from BoundedCollections import BoundedSet as ThirdPartBoundedSet. from PersistentCollections import PersistentSet as ThirdPartyPersistentSet, PersistentObject. type Set: interface generalized with (T) parameters consisting of Add: have (T) parameters returns nothing, Delete: have (T) parameters returns nothing, IsMember: have (T) parameters returns Boolean. type UnboundedSet: class implementing Set of Object and consisting of private ThirdPartySet: ThirdPartyUnboundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. type BoundedSet: class implementing Set of Object and consisting of private ThirdPartySet: ThirdPartyBoundedSet, public Add: subprogram with (Object) parameters returning nothing, public Delete: subprogram with (Object) parameters returning nothing, public IsMember: subprogram with (Object) parameters returning Boolean. type PersistentSet: class implementing Set of PersistentObject and consisting of private ThirdPartySet: ThirdPartyPersistentSet, public Add: subprogram with (PersistentObject) parameters returning nothing, public Delete: subprogram with (PersistentObject) parameters returning nothing, public IsMember: subprogram with (PersistentObject) parameters returning Boolean. 


()


. , , «» . , « — » . , . .


I — The Interface Segregation Principle


, . . .



, ? , - . , , SQL? , . , API . API , «interface», , . . O que poderia dar errado?


, , - , DBA . , 100500 , . . , .


, , , «» . , « interface, ». DBA . GDPR, HIPAA , .


- . , . E assim por diante


? , . :


, ( ).

? interface «interface_billing», «interface_customer_data» . .


, , . pet-project. IActor. , . , IActor : ICollidable, IDisplayble, IUpdatable. ?



( , Camera), . , - . , . , , IDisplayble SpecEffect.


CollisionsController , - ICollidable. , , , SOLID . TileWall -, . CollisionsController . , , IActor , .



: , , .


D — The Dependency Inversion


-. , . , , . , , . , , , , - . , , - . , , - .


Aqui! .» — - ImportantClass. , , . , ImportantClass VeryImportantClass, , , EvenMoreImportantClass, , . , , , . , . , .



ImportantClass VeryImportantClass EvenMoreImportantClass. ImportantClass . , , . , IVeryImportantClass IEvenMoreImportantClass, ImportantClass.


ImportantClass VeryImportantClass . ImportantClass « », IVeryImportantClass .



. , , .


. .
. .

, «» «» - . , , . . , , . , . , -.


. , .


- , . ( MegaSender), . , , SOAP API.


-. SenderAccess. , MegaSender SenderAccess . SenderAccess MegaSender, , MegaSender, MegaSender , Apple i.


MegaSender. LightSender. , SenderAccess c LightSender. , , . , .


SenderAccess, , MegaSender . , SenderAccess MegaSender. MegaSender « ». , MegaSender, , , . . , LightSender , , LightSender, MegaSender.


, SenderAccess , SenderAccess LightSender . .


, IActor ICollidable, IUpdatable, IDisplayble. , IActor . Actor Player, Enemy, Door, Wall . , .


Blueprint. . , , , , , et cetera. , , , .


, , C#, . , - List<String>. , List<T> List<String>. Actor Actor<TBlueprint>.


, , - . Actor<EnemyBlueprint> Actor<DoorBlueprint> , . , .


- . , , . , . , . , IActor, , ActorsFactory .


. : .



, , . - , . () :


. . . TCP/HTTP/SMPP/SOAP, . ? , TCP/HTTP/SMPP/SOAP- TCP/HTTP/SMPP/SOAP- , . , - . ? Pense nisso. , « » « 1000 ».



. ? - SOLID'? , . , - , .


- , .
- , .

. , -, , . . - , , , , -. , .


. - , :


KISS — . . . , , . , , .


, , Actor . , — , - . , .


YAGNI — . - , , « », , - . , . « » - . , , . , , .


, OC/P . 50 . , , -. 50 , . , - .


? , , « », . « » , .


, SOLID . , .



, . , -, , . , .


  • «Code Complete» . , « » - .
  • «Clean Code» . «Clean Architecture».
  • «Agile Principles, Patterns and Practices in C#» . SOLID . , language-agnostic.

PS


, . , , . : ! , , language-agnostic .


, language-agnostic. NEPL, : , , , . , , .


, . , , . :


, , , . , , . , . : , .

Source: https://habr.com/ru/post/pt446816/


All Articles