Entrada obligatoria
No puedo garantizar que las interpretaciones de los términos y principios generalmente aceptados establecidos aquí coincidan con lo que los profesores de California presentaron en artículos científicos sólidos en la segunda mitad del siglo pasado. No puedo garantizar que mis interpretaciones sean totalmente compartidas o compartidas por la mayoría de los profesionales de TI en la industria o comunidad académica. Ni siquiera puedo garantizar que mis interpretaciones lo ayuden en la entrevista, aunque supongo que serán útiles.
Pero garantizo que si la falta de comprensión es reemplazada por mis interpretaciones y empiezo a aplicarlas, entonces el código que escribió será más fácil de mantener y modificar. También entiendo muy bien que en los comentarios escribiré lo que se escribirá furiosamente, lo que me permitirá corregir omisiones e inconsistencias absolutamente evidentes.
Tales pequeñas garantías plantean preguntas sobre las razones por las cuales el artículo está escrito. Creo que estas cosas deberían enseñarse donde sea que enseñen programación, hasta lecciones de informática en las escuelas con un estudio en profundidad. Sin embargo, para mí se convirtió en una situación terriblemente normal cuando descubrí que el interlocutor es mi colega y que ha estado trabajando durante varios años, pero sobre la encapsulación "escuché algo allí". La necesidad de recopilar todo esto en un solo lugar y dar un enlace cuando surjan preguntas está maduro por mucho tiempo. Y luego mi "proyecto de mascota" me dio una buena cantidad de comida para pensar.
Aquí pueden objetarme que es demasiado temprano para aprender estas cosas en la escuela y, en general, la luz no se ha reunido en OOP. En primer lugar, depende de cómo aprendas. En segundo lugar, el 70% del material en este artículo se aplica no solo a OOP. Lo que notaré por separado.

OOP en pocas palabras
Esta es probablemente la sección más difícil para mí. Pero aún así, debe establecer la base y describir brevemente cuál es la esencia de la POO para comprender por qué la encapsulación, el polimorfismo, la herencia y los principios SÓLIDOS la fortalecieron. Y lo haré hablando de cómo podrías pensar en tal cosa.
Comenzó con Dijkstra, quien demostró que cualquier algoritmo se puede expresar de tres maneras para seleccionar el siguiente comando: ejecución lineal (en orden), ramificación por condición, ejecución de bucle mientras se cumple la condición. Usando estas tres conexiones, puede construir cualquier algoritmo. Además, se recomendó escribir programas, limitándose a la disposición lineal de comandos uno tras otro, ramificaciones y bucles. Esto se ha llamado "programación estructural de procedimiento " (gracias por la aclaración sshikov ).
También aquí notamos que la secuencia de comandos debe combinarse en subprogramas y cada subprograma puede ejecutarse usando un comando.
Además del orden de las acciones, es importante para nosotros aquello en lo que se realizan las acciones. Y se realizan en datos que se han acostumbrado a almacenar en variables. Las variables almacenan datos. Se interpretan según su tipo. Obviamente al rechinar de dientes, pero sea un poco paciente, por favor.
Desde el principio, se formó un conjunto más o menos general de tipos de datos primitivos. Enteros, números reales, variables booleanas, matrices, cadenas. Algoritmos + estructuras de datos = programas, como legó Nicklaus Wirth.
Además, desde el principio de los tiempos, había en diferentes formas un tipo de datos como un subprograma. O una pieza de código si quieres. Algunos podrían decir que usar subprogramas como variables es prerrogativa de la programación funcional. Aun así, la capacidad de hacer una pieza de código variable está incluso en ensamblador. Deje que esta posibilidad se reduzca a "bueno, aquí está el número de bytes en la RAM donde vive esta subrutina, y luego el comando LLAMAR con la pila de llamadas en los dientes y gire como pueda".
Por supuesto, varios tipos de datos eran pocos y la gente comenzó a pensar en agregar la capacidad de crear sus propios tipos a varios PL. Una variación de esta característica fueron las llamadas grabaciones. A continuación se muestran dos ejemplos de grabación en un lenguaje de programación inexistente (en adelante NEPL - Lenguaje de programación no existente):
type Name: record consisting of FirstName: String, MiddleName: String, LastName: String. type Point: record consisting of X: Double, Y: Double.
Es decir, en lugar de arrastrar dos o tres variables relacionadas, las agrupa en una estructura con los campos con nombre. Luego declara una variable de tipo Nombre y se refiere al campo Nombre, por ejemplo.
¿Qué es tan valioso en esta variable "mejorada" para nuestro tema? Que desde aquí solo hay un pequeño paso hacia la POO. No solo resalté un párrafo en negrita para indicar que los fragmentos de código también se pueden colocar en variables. Vea cómo las variables se convierten en 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 se está desarrollando activamente y ya ha reemplazado la palabra clave record con class.
Es decir, podemos acceder al campo "GetFullName" y llamarlo . Una variable contiene no solo datos que describen su estado, sino también su comportamiento. Por lo tanto, la variable se convierte en un objeto que tiene algunas habilidades y estado. Y ya estamos trabajando no solo con variables, sino con pequeños sistemas que pueden recibir comandos.
En mi juventud, esta idea me fascinó. Solo piense, puede crear cualquier tipo de datos . Y no estás trabajando con algunos números, sino con los objetos del mundo que estás creando. Sin tormentos con matrices aburridas o conjuntos intrincados de números. ¡Trabajamos directamente con objetos de los tipos Jugador, Enemigo, Bala, Jefe! Sí, en mi juventud quería hacer videojuegos.
En realidad, todo resultó no ser tan simple. Y sin algunas ideas "reforzadoras", OOP convertirá la vida de un programador en un infierno. Pero antes de continuar, demos algunos términos más:
- Los tipos de datos que están "reforzados" por su comportamiento en OOP se denominan clases .
- Las variables de estos tipos se denominan objetos .
- Y las rutinas que definen el comportamiento de los objetos se denominan métodos . Como regla general, cada clase tiene su propio conjunto de métodos, y no cada objeto. Para que cada objeto de una determinada clase se comporte como otros objetos de la misma clase. Me alegrará saber de los comentarios sobre idiomas en los que las cosas son diferentes.
Santa trinidad
Sucedió históricamente que preguntan sobre estas cosas en las entrevistas. Están escritos en cualquier libro de texto de idioma OOP. Por qué Porque si diseña un programa OOP sin tener en cuenta la encapsulación y el polimorfismo, obtendrá un "ataúd, ataúd, cementerio, sin acompañante". La herencia no es tan estrictamente necesaria, pero este concepto le permite comprender mejor la POO y es una de las principales herramientas para diseñar usando la POO.
Encapsulación
Bueno, comencemos con la definición de Wikipedia: "empaquetar datos y funciones en un solo componente". La definición parece clara, pero al mismo tiempo demasiado generalizada. Por lo tanto, hablemos acerca de por qué esto es necesario, qué nos dará y exactamente cómo empaquetar datos y funciones en un solo componente.
Ya escribí un artículo que trataba sobre la encapsulación. Y allí me reprocharon con razón el hecho de que reduje la encapsulación a la ocultación de información, y estas son cosas algo diferentes. En particular, EngineerSpock produjo una formulación tan elegante como la protección invariante . Admito mi error y luego explicaré por qué lo cometí.
Mientras tanto, mía, una definición preliminar del principio de encapsulación, o, si lo desea, el proceso de encapsulación, que describe no solo el principio de encapsulación, sino también lo que se supone que debe lograr con él:
Cualquier entidad de software con un estado no trivial debe convertirse en un sistema cerrado, que solo puede transferirse de un estado correcto a otro.
Sobre la parte donde "cualquier entidad de software que tiene un estado no trivial", pasemos un poco más tarde. Por ahora, hablaremos exclusivamente de objetos. Y sobre la segunda parte de mi definición. ¿Por qué necesitamos esto?
Aquí todo es simple: lo que solo se puede transferir de un estado correcto a otro no se puede romper. Es decir, debemos asegurarnos de que ningún objeto pueda romperse. Suena, por decirlo suavemente, ambicioso. ¿Cómo lograr esto?
En el cero, todo lo que se relaciona con el objeto debe estar en un lugar, dentro del mismo borde arquitectónico, digamos. En caso de que resulte muy abstruso, repetiré la definición de Wikipedia: "empaquetar datos y funciones en un solo componente".
Primero, separar claramente la interfaz y su implementación. Creo que todos mis colegas están familiarizados con la abreviatura API . Por lo tanto, cada objeto debe tener su propia API, o PI, si queremos ser meticulosos. Eso para lo que lo crean, y lo que otros usarán, lo que causarán. ¿Cómo debería ser? Para que a nadie se le ocurra entrar en un objeto y usarlo de manera inapropiada. Pero no más que eso.
En un libro, por desgracia, no recuerdo cuál, esto fue explicado por el ejemplo de un microondas. Hay botones en él. Plumas Te permiten calentar la comida. No necesita desenrollar el microondas y soldar algo allí para calentar la sopa de ayer. Tienes una interfaz, botones. Pon un plato, presiona un par de botones, espera un minuto y sé feliz.
Solo piense en los botones que debe presionar el usuario de su objeto y sepárelos de los menudillos internos. ¡Y en ningún caso no agregue botones adicionales! Ese fue el primero.
En segundo lugar, respete el límite entre la interfaz y la implementación y haga que otros lo respeten. En principio, esta idea es intuitiva y se encuentra entre la sabiduría popular en muchas formas. Tome al menos "si se aprovechó de algo indocumentado y luego algo se rompió para usted, usted tiene la culpa". Creo que con "no gire el microondas hasta que funcione como lo necesita", todo está claro. Ahora sobre cómo hacer que otros respeten la notoria frontera.
Aquí es donde la ocultación de la información viene al rescate. Sí, siempre puede aceptar, preguntar, configurar convenciones de código, señalar una revisión de código que esto no es posible. Pero la posibilidad misma de escalar más allá de esta frontera permanecerá. Esta es la ocultación de la información que viene al rescate.
No podemos cruzar la frontera notoria si nuestro código no puede aprender sobre su existencia y lo que hay detrás de ella. E incluso si se entera, el compilador fingirá que no existe tal campo o método, e incluso si lo hay, entonces no es necesario tocarlo, me niego a compilar, y generalmente quién es usted, no lo llamamos, use la parte de la interfaz.

Aquí es donde entran en juego todo tipo de modificadores de acceso públicos, privados y de otro tipo desde su idioma favorito. La "ocultación de información" es la forma más confiable de mantener los beneficios de la encapsulación en el drenaje. De todos modos, no tiene sentido agrupar todo lo que concierne a una clase en un solo lugar, si el código usa lo que quiere y dónde quiere. Pero con el ocultamiento de la información, tal situación ya no debería surgir en principio. Y este método es tan confiable que en la mente de miles y miles de programadores (incluido yo, que ya está allí), la diferencia entre encapsular y ocultar información se suaviza de alguna manera.
¿Qué sucede si su YP favorito no le permite ocultar información? Sobre este tema puedes divertirte hablando de comentarios. Veo la próxima salida. Ascendente:
- Documente solo la parte de la interfaz y considere todo lo que no está documentado como una implementación.
- Separe la implementación de la interfaz a través de la convención de código (ejemplo: en python existe la variable __todos__ que indica qué se importará exactamente del módulo cuando solicite importar todo).
- Haga que estas convenciones de código sean lo suficientemente estrictas para que puedan verificarse automáticamente, después de lo cual cualquier violación de ellas se equiparará con un error de compilación y una compilación caída.
Una vez mas:
- Todo lo relacionado con una clase está empaquetado en un módulo.
- Entre clases, se dibujan límites arquitectónicos estrictos.
- En cualquier clase, la parte de la interfaz está separada de la implementación de esta parte de la interfaz.
- ¡Los límites entre las clases deben ser respetados y obligados a respetar a los demás!
Terminaré con un ejemplo en NEPL, que todavía se está desarrollando de manera muy activa y, después de diez párrafos, ha adquirido modificadores de acceso:
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 quede claro en el código cuál es la esencia del ejemplo. Solo aclararé un punto: GetAccessToControlPanel comprueba si podemos tocar el microondas. ¿Y si ella está rota? Entonces no puedes hacer clic en nada. Solo puede recibir un mensaje de error.
Bueno, el hecho de que ButtonsPanel se haya convertido en una clase separada sin problemas nos lleva a una pregunta importante: ¿cuál es el "componente único" de la definición de encapsulación en Wikipedia? ¿Dónde y cómo deberían estar los límites entre las clases? Definitivamente volveremos a este tema un poco más tarde.
SpoilerPrincipio de responsabilidad única
Usar fuera de OOP
Muchos programadores han aprendido sobre la encapsulación en un tutorial de Java / C ++ / C # / sustituyen su primer lenguaje OOP. Por lo tanto, la encapsulación en la conciencia de masas de alguna manera está conectada con la POO. Pero volvamos a las dos definiciones de encapsulación.
Empaquetar datos y funciones en un solo componente.
Cualquier entidad de software con un estado no trivial debe convertirse en un sistema cerrado, que solo puede transferirse de un estado correcto a otro.
¿Te has dado cuenta? ¡No hay nada en absoluto sobre clases y objetos!
Entonces un ejemplo. Eres un DBA Su trabajo es vigilar algún tipo de base de datos relacional. Deja que sea, por ejemplo, en MySQL. Su preciosa base de datos utiliza varios programas. No tienes control sobre algunos de ellos. Que hacer
Crea un conjunto de procedimientos almacenados. Los compusimos en un circuito, que llamaremos interfaz. Creamos un usuario para nuestros programas sin ningún derecho. Este es el comando CREAR USUARIO. Luego, utilizando el comando GRANT, otorga a los usuarios solo el derecho de ejecutar estos procedimientos almacenados desde el esquema de la interfaz.
Eso es todo. Tenemos una base de datos, la misma entidad de software con un estado no trivial, que es lo suficientemente fácil de romper. Y para que no lo rompamos, creamos una interfaz a partir de procedimientos almacenados. Y después de los medios de MySQL, lo hacemos para que las entidades de terceros puedan usar solo esta interfaz.
Observe la notoria encapsulación, tal como es, y cómo se describió, se utiliza en todo su potencial. Pero existe una brecha entre la representación relacional de datos y objetos que debe cerrarse con marcos ORM voluminosos.
Es por eso que las clases y los objetos no aparecen en la definición de encapsulación. La idea es mucho más amplia que OOP. Y trae demasiado beneficio hablar en él solo en libros de texto en idiomas OOP.
Polimorfismo
El polimorfismo tiene muchas formas y definiciones. Es suficiente que Kondratius me baste cuando abro Wikipedia. Aquí hablaré sobre el polimorfismo, tal como lo formuló Straustrup: una interfaz, muchas implementaciones .
De esta forma, la idea del polimorfismo puede fortalecer en gran medida la posición de los escritores con un ojo para la encapsulación. Después de todo, si separamos la interfaz de la implementación, el que usa la interfaz no necesita saber que algo ha cambiado en la implementación. ¡Aquellos que usan la interfaz (idealmente) ni siquiera necesitan saber que la implementación ha cambiado en absoluto! Y esto abre infinitas posibilidades de expansión y modificación. ¿Su predecesor decidió que es mejor calentar la comida con un radar militar? Si este genio loco separó la interfaz de la implementación y la formalizó claramente, entonces el radar militar puede adaptarse a otras necesidades, y su interfaz para calentar alimentos puede realizarse utilizando un microondas.
NEPL se está desarrollando rápidamente y, bajo la influencia de C #, adquiere (cuidadosamente, no tropiece con la redacción) el tipo de interfaz de datos.
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.
Entonces, si se declara que una clase implementa una interfaz, entonces debe implementar todos los métodos desde esta interfaz. De lo contrario, el compilador nos dirá "fi". Y tenemos dos interfaces: FoodWarmer y FoodWarmerControlPanel. Míralos cuidadosamente y luego analicemos las implementaciones.
Como legado del difícil pasado soviético, recibimos el radar de clase de doble uso, que puede usarse para calentar alimentos y encontrar al enemigo. Y en lugar del panel de control, se usa una alarma, porque se ha excedido el plan y deben colocarse en algún lugar. Pero, afortunadamente, el MNS sin nombre del Instituto de Investigación de Química, Fertilizantes y Venenos, en el que se metieron, implementó las interfaces FoodWarmer para el radar y el Panel de Control FoodWarmer para el despertador.
Después de una generación, se le ocurrió a alguien que es mejor calentar la comida con un microondas, y es mejor controlar el microondas con botones. Y ahora las clases creadas de Microondas y ButtonsPanel. E implementan las mismas interfaces. FoodWarmer y FoodWarmerControl. ¿Qué nos da esto?
Si en todas partes de nuestro código usamos una variable como FoodWarmer para calentar los alimentos, simplemente podemos reemplazar la implementación por una más moderna y nadie notará nada. Es decir, al código que usa la interfaz no le importan los detalles de implementación. O al hecho de que ha cambiado por completo. Incluso podemos crear la clase FoodWarmerFactory que produce diferentes implementaciones de FoodWarmer dependiendo de la configuración de su aplicación.
También mire los campos cerrados en la clase de microondas y radar. Allí tenemos un reloj despertador y un panel con botones. Pero afuera damos una variable de tipo FoodWarmerControlPanel.
En algún lugar de Picabu había una historia sobre cómo un determinado candidato explicaba el principio del polimorfismo de la siguiente manera:
Aquí tengo un bolígrafo. Puedo escribirle mi nombre, pero puedo pegarlo en tu ojo. Este es el principio del polimorfismo.
La imagen es divertida, la situación es terrible y la explicación del principio del polimorfismo es inútil.
El principio del polimorfismo no es que una clase de bolígrafo con cierto miedo se dé cuenta de las interfaces de una papelería y acero frío al mismo tiempo. El principio del polimorfismo es que todo lo que se puede pinchar puede quedar atrapado en el ojo. Porque puede ser pinchado. Y el resultado será diferente, pero idealmente debería mostrar privación visual. Y el método del polimorfismo le permite reflejar este hecho en el modelo que está construyendo para su mundo.
Usar fuera de OOP
Hay un lenguaje tan divertido y divertido en todos los sentidos de estas palabras como Erlang. Y tiene una característica como el comportamiento. Cuida tus manos:
El código allí está dividido en módulos. Puede usar el nombre del módulo como variable. Es decir, puede escribir una llamada de función desde un módulo como este:
Para asegurarse de que el módulo tenga ciertas funciones, existe una característica del lenguaje como el comportamiento. En un módulo que usa otros módulos, usted define los requisitos para módulos variables usando la declaración behaviour_info. Y luego, los módulos que usará su módulo papá, que configuran behaviour_info, usando la declaración de comportamiento, le dicen al compilador: "nos comprometemos a implementar este comportamiento para que el módulo papá pueda usarnos".
Por ejemplo, el módulo gen_server le permite crear un servidor que sincrónicamente o asincrónicamente en un proceso separado (no hay hilos en Erlang, solo miles de pequeños procesos paralelos), ejecuta solicitudes de otros procesos. Y en gen_server se recopila toda la lógica relacionada con las solicitudes de otros procesos. Pero el procesamiento directo de estas solicitudes lo realizan quienes implementan el comportamiento de gen_server. Y mientras que otros módulos lo implementan correctamente (incluso si hay apéndices vacíos), a gen_server ni siquiera le importa cómo se manejan estas solicitudes. Además, el módulo de procesamiento se puede cambiar sobre la marcha.
Una interfaz: muchas implementaciones. Como Straustrup nos legó. Como está escrito en libros inteligentes sobre OOP. Y ahora una cita de Wikipedia al estudio:
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. Por qué , Shape, .
. , ? -, . -, , , , . , , .
, . , . « »? , . . , .
. , . , , . . , . , , .
, , . . ( , ), , . , , . , , . .
, ) , ) , , , 999 1000 . , , .
()
, . - , . , , . - , .
SOLID
— , , . - . SOLID — , , … ? ? , , .
S — The Single Responsibility Principle
. .
.
. . .
, « » - . . « ?». , , ? .
, SRP :
.
, ? — , . ? . « ». , . ? .
, ? . , .txt-. , , , .txt-. - , . , , … .txt-. Por qué . , , .txt-.
NB ( , ) «», « , ».

. , , , .txt-. . , . , . Pero! -, , , . -, , , .

, , . , ) , ) .html-. , , .txt/.html.
, , , . , , .txt-. ¿Qué podría salir mal?
- . , . , , . , , , , , , . .
- , . 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 /. , : .
- , . , .
- , - , , . (), () «» .
- . , , ? , . , . , .
, . .
Mal
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#.
, NEPLNEPL. 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», , . . ¿Qué podría salir mal?
, , - , DBA . , 100500 , . . , .
, , , «» . , « interface, ». DBA . GDPR, HIPAA , .
- . , . Y así sucesivamente.
? , . :
, ( ).
? 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
-. , . , , . , , . , , , , - . , , - . , , - .
"¡Aquí! .» — - 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- , . , - . ? Piensa en eso. , « » « 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, : , , , . , , .
, . , , . :
, , , . , , . , . : , .