¿La sobrecarga es un ejemplo del principio abierto / cerrado?

12

Wikipedia dice

"las entidades de software (clases, módulos, funciones, etc.) deben estar abiertas para la extensión, pero cerradas para la modificación"

La palabra funciones me llamó la atención, y ahora me pregunto si podemos suponer que crear una sobrecarga para un método puede considerarse como un ejemplo del principio Abierto / cerrado o no.

Déjame explicarte un ejemplo. Considere que tiene un método en su capa de servicio, que se utiliza en casi 1000 lugares. El método obtiene userId y determina si el usuario es administrador o no:

bool IsAdmin(userId)

Ahora considere que en algún lugar es necesario determinar si el usuario es administrador o no, según el nombre de usuario, no userId. Si cambiamos la firma del método mencionado anteriormente, entonces hemos roto el código en 1000 lugares (las funciones deben cerrarse para su modificación). Por lo tanto, podemos crear una sobrecarga para obtener el nombre de usuario, encontrar el ID de usuario basado en el nombre de usuario y el método original:

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

De esta manera, hemos ampliado nuestra función mediante la creación de una sobrecarga (las funciones deben estar abiertas a la extensión).

¿Es un ejemplo de principio abierto / cerrado?

Saeed Neamati
fuente

Respuestas:

5

Personalmente interpretaría la declaración wiki de esta manera:

  • para una clase: siéntase libre de heredar la clase y anular o ampliar su funcionalidad, pero no piratee la clase original cambiando así lo que hace.
  • para un módulo (tal vez una biblioteca, por ejemplo): siéntase libre de escribir un nuevo módulo / biblioteca que envuelva el original, combine funciones en versiones más fáciles de usar o extienda el original con características adicionales, pero no cambie el módulo original.
  • para una función (es decir, una función estática, no un método de clase): su ejemplo es razonable para mí; reutilice la función original IsAdmin (int) dentro de la nueva IsAdmin (cadena). el original no cambia, el nuevo func amplía su funcionalidad.

Sin embargo, el tiro en el pie es que si su código está usando la clase 'cUserInfo' en la que reside IsAdmin (int), esencialmente está rompiendo la regla y cambiando la clase. lamentablemente, la regla solo se mantendría si crea una nueva clase cUserWithNameInfo: clase public cUserInfo y coloca su anulación IsAdmin (cadena) allí. Si fuera dueño del código base, nunca obedecería la regla. Yo diría bollocks y solo hago el cambio que sugieres.

Sassafras_wot
fuente
3

En primer lugar, su ejemplo es lamentablemente artificial. Nunca harías eso en el mundo real, porque causa un doble intento innecesario. O, peor aún, porque userid y username pueden convertirse en el mismo tipo en algún momento. Más bien que

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

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

Realmente deberías extender esa clase.

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

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

Puede refactorizar y cambiar el nombre del primer método a IsUserIdAdmin, por razones de coherencia, pero no es necesario. El punto es que, al agregar el nuevo método y garantizar que no se rompa ningún código de llamada, ha encontrado que su clase es extensible. O en otras palabras, abierto a la extensión .

Y aquí está la cosa con el principio abierto-cerrado. Nunca se sabe qué tan bien se ajusta su código hasta que intente extender parte de él y tenga que modificarlo (con el mayor riesgo que conlleva). Pero, con experiencia, aprendes a predecir.

Como dice el tío Bob (énfasis mío):

Como el cierre no puede ser completo, debe ser estratégico. Es decir, el diseñador debe elegir los tipos de cambios para cerrar su diseño. Esto requiere una cierta cantidad de presciencia derivada de la experiencia . El diseñador experimentado conoce a los usuarios y la industria lo suficientemente bien como para juzgar la probabilidad de diferentes tipos de cambios. Luego se asegura de que se invoque el principio abierto-cerrado para los cambios más probables.

pdr
fuente
Gracias por tu buena explicación. Pero tener 1000 viajes de ida y vuelta o uno de ida y vuelta no es el punto principal aquí. Por lo tanto, olvidemos el rendimiento en este momento. Sin embargo, lo que hice fue también extender la clase, porque le agregué otro método, aunque con el mismo nombre, pero con diferentes parámetros de entrada. Pero realmente aprecio tu cita del tío Bob . +1. ;)
Saeed Neamati
@SaeedNeamati: El punto que estaba señalando es que no importa si está agregando un método que usa un método existente o un método que es completamente independiente, de cualquier manera su clase estaba abierta a la extensión. Pero, si tuvo que modificar la interfaz del método existente para lograrlo, entonces no está cerrado a la modificación.
pdr
2

Los módulos que se ajustan al principio abierto-cerrado tienen dos atributos principales.

  1. Están "abiertos para la extensión". Esto significa que el comportamiento del módulo puede extenderse. Que podemos hacer que el módulo se comporte de formas nuevas y diferentes a medida que cambian los requisitos de la aplicación, o para satisfacer las necesidades de las nuevas aplicaciones.
  2. Están "cerrados por modificación". El código fuente de dicho módulo es inviolable. Nadie puede hacer cambios en el código fuente.
  1. Al sobrecargar su método, está ampliando la funcionalidad del módulo existente, satisfaciendo así las nuevas necesidades de su aplicación

  2. No está realizando ningún cambio en un método existente, por lo que no está violando la segunda regla.

Me interesaría saber lo que otros tienen que decir sobre no permitir cambiar el método original. Sí, puede colocar modificadores de acceso apropiados y aplicar la encapsulación, pero ¿qué más se puede hacer?

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

CodeART
fuente
1

El Principio Abierto-Cerrado es una meta, un caso ideal, no siempre una realidad. Particularmente cuando se busca refactorizar el código antiguo, el primer paso suele ser una gran modificación para hacer posible la OCP. La raíz del principio es que el código de trabajo ya funciona, y el cambio posiblemente introduce errores. Por lo tanto, el mejor escenario es no cambiar el código existente, solo agregar un código nuevo.

Pero digamos que tenía una función llamada BigContrivedMethod(int1, int2, string1). BigContrivedMethodhace tres cosas: cosa1, cosa2 y cosa3. En este punto, reutilizar BCM es probablemente difícil, porque hace demasiado. Refactorizándolo (si es posible) en ContrivedFunction1(int), ContrivedFunction2(int)y ContrivedFunction3(string)le brinda tres métodos más pequeños y más enfocados que puede combinar más fácilmente.

Y esa es la clave de OCP en lo que respecta a métodos / funciones: composición. Usted "extiende" las funciones al llamarlas desde otras funciones.

Recuerde que OCP es parte de otros 5 principios, las pautas SÓLIDAS. El primero es clave, la responsabilidad única. Si todo en su base de código solo hiciera lo único que tenía que hacer, nunca necesitaría modificar el código. Solo necesitaría agregar un código nuevo o combinar el código viejo de formas nuevas. Dado que el código real rara vez cumple con esa directriz, a menudo tiene que modificarlo para obtener SRP antes de poder obtener OCP.

CodexArcanum
fuente
Creo que aquí se habla del Director de Responsabilidad Individual, no de OCP.
Saeed Neamati
1
Estoy diciendo que no puedes tener OCP de manera realista sin SRP. El código que hace demasiado no se puede extender, no se puede reutilizar. SOLID está casi escrito en orden de importancia, y cada principio depende de los anteriores para que sea factible.
CodexArcanum
Esta respuesta debería ser votada más.
Ashish Gupta
0

Está siguiendo el principio abierto-cerrado si está alterando el comportamiento de su programa escribiendo un código nuevo en lugar de alterar el código antiguo.

Dado que es bastante imposible escribir código que esté abierto a todos los cambios posibles (y no desea hacerlo porque ingresa la parálisis de análisis), escribe un código que responde a todos los diferentes comportamientos en los que está trabajando actualmente por extensión en lugar de modificación.

Cuando te encuentras con algo que necesitas cambiar, en lugar de simplemente alterar algo para permitir el nuevo comportamiento, descubres cuál es el punto de cambio, reestructura tu programa sin alterar su comportamiento para que luego puedas cambiar ese comportamiento escribiendo NUEVO código .

Entonces, cómo se aplica esto a su caso:

Si está agregando nuevas funciones a su clase, entonces su clase no está abierta / cerrada, pero los clientes de la función que está sobrecargando sí lo están.

Si simplemente está agregando nuevas funciones que funcionan en su clase, tanto ella como los clientes de su función están abiertos / cerrados.

Edward extraño
fuente