2012-05-23 5 views
12

Estoy tratando de comprender el principio Abierto/Cerrado (en mi caso para PHP, pero eso realmente no debería marcar la diferencia).Principio abierto/cerrado - ¿Cómo llamar a las nuevas versiones?

La manera en que lo entiendo es que una clase es nunca abierto para la modificación. Solo para corregir errores. Si quisiera agregar un nuevo código a la clase, entonces tendría que crear uno nuevo y extender la clase 'vieja'. Esa es la única forma en que puedo agregarle un código nuevo.

De alguna manera puedo ver las ventajas de esto. Porque básicamente creas algún tipo de sistema de control de versiones, donde el código antiguo siempre funciona, pero siempre puedes intentar usar las nuevas clases también.

Pero, ¿cómo funciona esto en la práctica? Es decir, supongamos que tengo la siguiente clase:

class MyObject 
{ 
    public function doSomething() 
    { 
     echo 'Im doing something'; 
    } 
} 

Así que estoy en algún lugar, probablemente, una instancia de esta clase:

$obj = new MyObject(); 

Pero entonces decido que es bueno tener otro método de ese objeto. Entonces puedo hacer algo más también. De acuerdo con el OCP, no puedo modificar la clase. Entonces tengo que crear uno nuevo, que se extienda a uno viejo, ¿verdad?

Primer problema. ¿Cómo llamo a la nueva clase? Porque no es realmente un objeto completamente nuevo. Me gusta. un objeto Usuario es un objeto Usuario. No puedo darle un nombre completamente diferente simplemente porque necesita otro método. De todos modos, i crear la nueva clase:

class MyNewObject extends MyObject 
{ 
    public function doSomethingElse() 
    { 
     echo 'Im doing something else now'; 
    } 
} 

Ahora bien, esto también significa que tengo que cambiar la línea de código en el que crea una instancia de la clase "MiObjeto" y sustituirla por la clase "MyNewObject", a la derecha ..? Y si eso se hace en más de un lugar, entonces tengo que buscar a través de mi código fuente ... (Piense en un método en una clase de controlador, que casi siempre usa la palabra clave 'nueva' para crear instancias de ciertas clases).

Lo mismo aplica básicamente a la herencia. Tendría que encontrar que cada clase hereda la clase anterior y debo reemplazarla con la nueva clase.


Así que, básicamente, mis preguntas son:

¿Cómo se nombra las nuevas clases que tienen los nuevos métodos? Solo porque agregué algunas funcionalidades nuevas, no significa que pueda darle a la clase un nombre completamente nuevo ...

¿Y si las 'viejas' clases se instancian (o heredan) de múltiples lugares? Entonces tendría que encontrar todos esos lugares ... ¿Dónde está la ganancia?

+1

'$ x = new MyObj_version_3.14159265()'? mmmmm. tarta. –

+1

Tenga en cuenta que hay dos formas de interpretar el [Principio abierto/cerrado] (http://en.wikipedia.org/wiki/Open/closed_principle). Pareces estar pensando principalmente en el más antiguo (Meyers), mientras que ese caso de uso generalmente se maneja por delegación hoy en día. –

+0

Sería más fácil responder a esta pregunta con un ejemplo más realista. Nombrar, etc. sería más obvio, creo. – Fuhrmanator

Respuesta

3

El principio de apertura cerrada no está destinado a ser utilizado como un tipo de sistema de control de versiones. Si realmente necesita hacer cambios en la clase, siga adelante y realice esos cambios. No necesita crear nuevas clases y cambiar todos los lugares que crearon la instancia del anterior.

El objetivo del principio de Open Closed es que un sistema bien diseñado no debería requerir que cambie la funcionalidad existente para agregar nuevas funcionalidades. Si agrega una nueva clase al sistema, no debería necesitar buscar todo su código para encontrar los lugares donde necesita hacer referencia a esa clase o tener casos especiales para ello.

Si el diseño de su clase no es lo suficientemente flexible como para manejar alguna nueva funcionalidad, entonces cambie el código de su clase. Pero cuando cambia el código, hágalo flexible para que pueda manejar cambios similares en el futuro sin cambios de código. Está destinado a ser una política de diseño, no un conjunto de esposas para evitar que realice cambios. Con buenas decisiones de diseño, con el tiempo su código existente requerirá cada vez menos cambios cuando agregue nuevas funcionalidades al sistema. Es un proceso iterativo.

0

Es necesario crear un constructor en la clase MyNewObject el que llama a la clase constructor de los padres:

function __construct() { 

    parent::__construct(); 

} 

esta manera se puede crear una instancia de la nueva clase y todavía tener acceso a toda la funcionalidad de la ampliada.

También puede anular cualquier función en la clase principal (siempre que no esté marcada como definitiva por supuesto).

por lo que podría hacer:

$newObj = new MyNewObject(); 

$newObj->doSomething(); 

$newObj->doSomethingElse(); 
+0

Sí, sé que tengo que llamar al constructor de los padres. De lo contrario, la herencia no sería de mucha utilidad :) --- Pero en tu ejemplo también estás creando una instancia de "MyNewObject" en lugar de "MyObject". Lo cual hace que tenga sentido, por supuesto, porque quiere usar la nueva funcionalidad. Pero eso también significa que tienes que cambiar cada línea en tu código donde una vez instanciaste "MyObject". Tendría que cambiar eso ahora a "MyNewObject". Esto realmente no me parece demasiado difícil si tiene que buscar todo el código para cambiar las instancias anteriores a la nueva. – w00

+0

Sí, veo tu punto. Perdóname por asumir que eras un poco nuevo en OOP. – Steven1978

3

Yo diría que mediante la adición de una función, usted no está modificando el comportamiento de la clase.

En todos los casos en que doSomething() se está llamando en su aplicación, simplemente agregando doSomethingElse() a la clase no tendrá ningún efecto. Como no está cambiando doSomething(), el comportamiento es el mismo que antes.

Una vez que determina que su implementación doSomething() no lo está recortando para ciertas circunstancias, puede extender la clase y anular doSometing(). Una vez más, el original todavía se comporta igual que siempre, pero ahora tiene un nuevo doSomething() para trabajar también.

Me doy cuenta de que esto va en contra de la definición estricta de abierto/cerrado, pero este es el mundo real, y así es como interpreté ese principio en mi código.

+0

Acepto, agregar un nuevo método no es tan rompedor. No en el ejemplo que di. Pero si necesito hacer algunos cambios en el código de un método, entonces tendría que crear una nueva clase que haga lo que yo quiera. Pero entonces mis dos preguntas aún permanecen. ¿Cómo puedo nombrar la nueva clase? ¿Y eso significa que tengo que buscar todo mi código para averiguar dónde instalé la clase anterior para poder cambiarla con la nueva? El último es el que más me molesta en realidad ... Por lo tanto, no veo beneficio en este principio. – w00

+1

Deberías pensar en esto al revés: si tu subclase no tiene un nombre obvio, entonces ¡quizás no deberías hacerlo!Las subclases no deben usarse para arreglar clases rotas: la clase rota debe ser reparada, y el código de llamada que depende del comportamiento roto también debe ser corregido. (Dicho esto, a veces es necesario crear una subclase para arreglar una clase interrumpida, pero eso generalmente se debe a que no se puede controlar la clase padre o su código de llamada). –

+1

Por cierto, dos buenas razones para hacer una subclase: hacer una nueva implementación de una clase base más abstracta (herencia polimórfica), o para crear un subtipo que tenga una funcionalidad extra pero que obedezca el [Principio de sustitución de Liskov] (http://en.wikipedia.org/wiki/Liskov_substitution_principle). En ambos casos, un nuevo nombre debería ser obvio. –

Cuestiones relacionadas