En este artículo trataré de hablar sobre el principio de diseño llamado Inversión de control / IoC, también llamado el principio de Hollywood.
Mostraré cómo se relaciona esto con el
principio de sustituir a Barbara Liskovo (LSP) , así como contribuir a la guerra santa privada vs protegida.

Como prefacio, quiero decir algunas palabras sobre mí. Soy ingeniero de software de formación, he trabajado en la industria de TI durante más de 10 años y recientemente me ha gustado escribir artículos profesionales temáticos. Algunos de ellos tuvieron éxito. Anteriormente, publiqué en otro recurso, desafortunadamente, inaccesible en Rusia (saludos a Roskomnadzor). Si alguien quiere conocerlos, usted sabe qué hacer.
Todos los ejemplos de código, como de costumbre, se presentan en el artículo con pseudocódigo estilizado como "odiado php".
Tarea inicial
Para hacerlo más rápido y más comprensible, procedemos inmediatamente al ejemplo. Desde el departamento de ventas, quería ver las métricas: cuánto dinero ganamos mensualmente, diariamente, cada hora.
Resolvemos este problema con la ayuda de tres equipos que se ejecutan periódicamente en un horario:
- MonthlyReportCommand
- DailyReportCommand
- HourlyRerortCommand
Necesitaremos las interfaces:
interface ReportCommandInterface { public function createReport(): Money; } interface MoneyRepositoryInterface { public function getMoney(Period $period): array; } interface MetricRepositoryInterface { public function saveMoneyMetric(Period $period, Money $amount, string $metricType); }
Escribimos equipos de informes (este último se omite, como ejercicio práctico para aquellos que quieren entender y practicar bien, escríbalo usted mismo):
class MonthlyReportCommand implements ReportCommandInterface {
Y vemos que el código del método CalculateTotals () será exactamente el mismo en todos los casos. Lo primero que viene a la mente es poner código duplicado en una clase abstracta común. Así:

abstract class AbstractReportCommand { protected function calculateTotals(array $moneyRecords): Money {
El método CalculateTotals () es parte de los mecanismos internos de nuestra clase. Lo cerramos con prudencia, porque no debe ser llamado por clientes externos; no lo estamos diseñando para esto. Declaramos este método protegido, porque Tenemos la intención de llamarlo herederos, este es nuestro objetivo. Obviamente, una clase tan abstracta es muy similar a algo así como una biblioteca: solo proporciona algunos métodos (para expertos en php: es decir, funciona como Trait).
El secreto de las clases abstractas.
Es hora de tomar un descanso del ejemplo y recordar el propósito de las clases abstractas:
Una clase abstracta encapsula mecanismos generales, al mismo tiempo que permite a los herederos implementar su propio comportamiento particular.La abstracción (lat. Abstractio - distracción) es una distracción de los detalles y la generalización. Por el momento, la clase AbstractReportCommand generaliza solo el recuento de dinero para todos los informes. Pero podemos hacer que nuestra abstracción sea más eficiente utilizando el principio de Hollywood, que suena así:
"No nos llames, te llamaremos nosotros mismos"Para ver cómo funciona esto, pongamos en AbstractReportCommand un mecanismo general de informes:

abstract class AbstractReportCommand implements ReportCommandInterface { private $moneyRepository; private $metricRepository;
Que hicimos Ninguno de los descendientes de una clase abstracta se aplica a mecanismos comunes (no nos llame). En cambio, la abstracción le da a sus herederos un esquema general de funcionamiento y les exige que implementen características de comportamiento particulares, utilizando solo los resultados (lo desafiaremos).
Pero, ¿qué pasa con el prometido IoC, LSP, privado vs protegido?
Entonces, ¿qué tiene que ver la Inversión de control? ¿De dónde viene este nombre? Muy simple: primero establecemos la secuencia de llamadas directamente en las implementaciones finales, controlando qué se hará y cuándo. Y luego, transferimos esta lógica a una abstracción general. Ahora la abstracción controla qué y cuándo se llamará, y las implementaciones simplemente obedecen esto. Es decir, invertimos el control.
Para corregir este comportamiento y evitar problemas con el
principio de sustitución de Barbara Liskov (LSP) , puede cerrar el método createReport () incluyendo final en la declaración del método. Después de todo, todos saben que el LSP está directamente relacionado con la herencia.
abstract class AbstractReportCommand implements ReportCommandInterface { final public function createReport(): Money {
Luego, todos los descendientes de la clase AbstractReportCommand se vuelven rígidamente subordinados a una única lógica que no se puede redefinir. Disciplina de hierro, orden, futuro brillante.
Por la misma razón, la ventaja de lo privado sobre lo protegido se hace evidente. Todo lo relacionado con los mecanismos generales de funcionamiento debe estar conectado en una clase abstracta y no accesible para la redefinición: privado. Todo lo que necesita redefinirse / implementarse en casos especiales está protegido por resúmenes. Cualquier método está diseñado para propósitos específicos. Y si no sabe qué tipo de alcance establecer para un método, significa que no sabe por qué lo está creando. Vale la pena revisar este diseño.
Conclusiones
La construcción de clases abstractas siempre es preferible con el uso de Inversión de control, ya que le permite utilizar la idea de abstracción al máximo. Pero el uso de clases abstractas como bibliotecas en algunos casos también puede justificarse.
Si miras de manera más amplia, nuestra confrontación en un pueblo pequeño entre el principio de Hollywood y la clase de biblioteca abstracta se convierte en una disputa: un marco (IoC para adultos) frente a una biblioteca. No tiene sentido probar cuál de ellos es mejor: cada uno se crea para un propósito específico. Lo único importante es la creación consciente de tales estructuras.
Gracias a todos los que leyeron cuidadosamente de principio a fin: ustedes son mis lectores favoritos.