studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones c# oop object-oriented-analysis

c# - para - manual de programacion android pdf



Declarar niños tipo en la clase base; ¿Es malo o no? (6)

Creo que el problema se puede resolver cuando volvamos a la filosofía de cuándo utilizar la herencia .
Se dice que " pensar en la herencia como una es una relación "

Cuando establece una relación de herencia entre dos clases, puede aprovechar el enlace dinámico y el polimorfismo.
Enlace dinámico significa el tiempo de ejecución en el que la implementación del método invocar, en función de la clase del objeto.
Polimorfismo significa que puede usar una variable de un tipo de superclase para mantener una referencia a un objeto cuya clase es la superclase o cualquiera de sus subclases.

Uno de los principales beneficios de la unión dinámica y el polimorfismo es que pueden ayudar a que el código sea más fácil de cambiar. Si tiene un fragmento de código que utiliza una variable de un tipo de superclase, como Empleado, más tarde podría crear una nueva subclase, como Administrador, y el fragmento de código antiguo funcionará sin cambios con las instancias de la nueva subclase. Si el Administrador anula cualquiera de los métodos del Empleado que son invocados por el fragmento de código, el enlace dinámico garantizará que se ejecute la implementación de esos métodos por parte del Gerente. Esto será cierto aunque el Administrador de clases no existiera cuando el fragmento de código se escribió y compiló.

Ahora volviendo a la pregunta, ¿por qué necesitamos un EmployeeType en Employee?
Puede haber funciones que nos gustaría implementar en Employee, AKA CalculateSalary ()

CalculateSalary(){ if (EmployeeType == EmployeeType.Manager) // Add management percent }

Razón tentadora, pero ¿y si el Empleado es tanto un Gerente como un Supervisor?
¡Va a ser complicado! Simplemente podemos implementar CalculateSalary () como un método abstracto, ¡pero perderemos el objetivo!
Aquí viene la Regla de Coad:
Una subclase expresa es un tipo especial de y no es un rol jugado por a .
El principio de sustitución de Liskov es una prueba para ''¿Debo heredar de este tipo?''

El papel del pulgar para lo anterior es:

  • ¿Desea TypeB exponer la interfaz completa (todos los métodos públicos no menos) de TypeA de modo que TypeB se pueda usar donde se espera TypeA? Indica Herencia.
    por ejemplo, un biplano Cessna expondrá la interfaz completa de un avión, si no más. Así que eso hace que sea apropiado derivar de Avión.
  • ¿TypeB solo quiere solo una parte del comportamiento expuesto por TypeA? Indica necesidad de composición.
    Por ejemplo, un pájaro puede necesitar solo el comportamiento de vuelo de un avión. En este caso, tiene sentido extraerlo como una interfaz / clase / ambos y hacer que sea un miembro de ambas clases.

Como una nota de pie de javaworld :
Asegúrese de que la herencia modela la relación is-a.
No utilice la herencia solo para obtener la reutilización del código.
No use la herencia solo para llegar al polimorfismo.
Favorecer la composición sobre la herencia.

Creo que en situaciones como esta, ser un administrador es un rol desempeñado por el empleado, ese rol puede cambiar con el tiempo, el rol se implementaría como una composición.

Como otro aspecto de la vista en el modelo de dominio, y asignarlo a una base de datos relacional. Es realmente difícil mapear la herencia al modelo SQL (terminas creando columnas que no siempre se usan, usando vistas, etc.). Algunos ORM intentan lidiar con esto, pero siempre se complica rápidamente.
Por ejemplo, el enfoque de una tabla por clase de Hibernate, que viola 2NF y podría causar una inconsistencia muy probable para su muestra de

Secretary secretary = new Secretary(); secretary.EmployeeType = EmployeeType.Manager;


Recientemente, encontré un código que ha declarado los tipos de niños como una enumeración en la clase base. Aquí hay un ejemplo simple:

public enum EmployeeType { Manager, Secretary } public class Employee { public string Code { get; set; } public EmployeeType Type { get; set; } } public class Manager : Employee { public void Manage() { // Managing } } public class Secretary : Employee { public void SetMeeting() { // Setting meeting } }

Sobre la base de mi experiencia en desarrollo, escribí un artículo al respecto, declarando que es una mala práctica / diseño. Creo que es malo porque la clase base debería ser agnóstica acerca de sus clases para niños. No debería tener información sobre sus clases para niños, y aquí hay al menos dos razones:

  1. Extensibilidad: este diseño no será extensible, porque si desea definir otra clase derivada, digamos Developer por ejemplo, también debe actualizar la enumeración de EmployeeType , a la que tal vez no tenga acceso.
  2. Definición paradójica: ahora puedes escribir este código:

    Secretary secretary = new Secretary(); secretary.EmployeeType = EmployeeType.Manager; /* This is absurd semantically. But syntactically, it''s possible. */

Sin embargo, mientras leía el artículo de Wikipedia sobre herencia , no pude encontrar ninguna respuesta a mi pregunta.

Si bien puede parecer discutible a primera vista, creo que la herencia debería ser lo suficientemente madura como para tener una respuesta sólida para este dilema. ¿Es este código malo, código maloliente ? ¿O es aceptable y justificado? ¿Por qué?


Creo que es un buen diseño para ciertos escenarios, y ciertamente es un patrón recurrente visto en varias partes de la biblioteca de clases base .NET.

Creo que la idea falsa que implica que podría ser un mal diseño es que las jerarquías de clase deben ser extensibles. Sin embargo, ante todo, una relación de herencia solo expresa que un subtipo es una especialización de un supertipo, y que el subtipo no es el mismo que el supertipo, pero es compatible con el supertipo (interfaz del).

Una familia de tipos relacionados por herencia no ofrece necesariamente ninguna posibilidad para que terceros agreguen ningún tipo adicional. Este es a menudo, pero no exclusivamente, el caso cuando tal tipo de familia representa las estructuras de datos definidas en un estándar.

En cuanto a expresar el tipo de la clase actual con un valor enum , nada habla en contra de eso si la jerarquía de herencia se limita a la extensión para terceros. Estrictamente hablando, tal propiedad no debería ser necesaria, pero como los idiomas como C # no ofrecen ninguna posibilidad de cambiar de tipo, ofrecer un valor enum mejora la legibilidad del código que tiene que manejar los diferentes tipos de la familia de tipos y, por lo tanto, constituye una buena opción. Diseño de la API. En este sentido, también mejora la capacidad de mantenimiento, ya que las versiones futuras de la misma familia de tipos podrían reestructurar la jerarquía de tipos. El código que se compara exclusivamente con los valores de enum será independiente de dichos cambios (y las soluciones alternativas propensas a errores, como la comparación con el nombre de tipo ( string ) en lugar del objeto Type sí, no se presentarán en primer lugar).

El .NET BCL usa este patrón en varios lugares, tales como:


De la pregunta que parece, EmployeeType coincide con una clase. En ese caso, no es la respuesta simplemente, ese tipo de empleado es el tipo de la clase. No debería ser una propiedad en absoluto, y tener una enumeración de tipos parece inútil. Una lista o matriz tal vez, pero no enumeración. Los detalles exactos dependen de cuál es el caso de uso y el diseño general, exactamente. O, pensándolo bien, probablemente debería haber una propiedad de solo lectura para el tipo, que otorga al tipo público, posiblemente abstracto, incluso si el tipo real de instancia de empleado es privado.

Entonces el método de fábrica debe tomar el tipo de clase empleado. Tenga en cuenta que el tipo visible en API puede ser abstracto, y el método de fábrica puede elegir el tipo concreto exacto internamente. Además, una versión del método de fábrica que toma un tipo de cadena es probablemente útil.

Sin embargo, si el EmployeeType no corresponde a una clase con el mismo nombre en cuestión, entonces la respuesta es ligeramente diferente. En primer lugar, debe usar nombres diferentes, de modo que no haya riesgo de confundirse con la enumeración y la clase. Segundo, podría valer la pena convertir EmployeeType a una clase o interfaz. El empleado entonces tiene un tipo, en lugar de ser un tipo. Composición sobre herencia.


Definitivamente es una mala práctica. Dejando de lado el dudoso uso de la herencia (que en sí se considera ampliamente como una mala práctica, en comparación con el uso de la composición), rompe el principio de inversión de dependencia al introducir una dependencia circular desagradable entre las clases de padres e hijos.

Al rediseñar el código, esperaría abordar tres problemas:

  1. Quitaría la dependencia circular, eliminando la enumeración de la clase base.
  2. Haría de Employee una interfaz.
  3. Me gustaría eliminar los setters públicos, que permiten que cualquier aspecto de la aplicación modifique aspectos centrales del registro del empleado, lo que hace que las pruebas y el seguimiento de errores sean mucho más difíciles de lo que deberían ser.

Estoy de acuerdo en que esta es una mala práctica, pero en ciertos escenarios esto podría ser necesario o útil y, de ser así, al menos puede evitar que algo malo suceda haciendo que la propiedad de la clase base sea de solo lectura y establecerla a través de ctor de las subclases.

public enum EmployeeType { Manager, Secretary } public class Employee { public Employee(EmployeeType type) { this.Type = type; } public string Code { get; set; } public EmployeeType Type { get; private set; } } public class Manager : Employee { public Manager() : base(EmployeeType.Manager) { } public void Manage() { // Managing } } public class Secretary : Employee { public Secretary() : base(EmployeeType.Secretary) { } public void SetMeeting() { // Setting meeting } }

Para evitar que el empleado se cree una instancia con un tipo incorrecto, puede hacerlo protegido.

En general, esta pregunta es bastante amplia, lo que significa que realmente depende del diseño general y trataría de evitar que esto suceda refactorizando / rediseñando las estructuras de clase y / o la lógica que espera esa propiedad de Tipo.


Suena mal "Código olor", diría mal diseño. Pero normalmente hago algo como esto y creo que es aceptable.

public enum EmployeeType { UnKnown,//this can be used for extensiblity Manager, Secretary, } public abstract class Employee//<--1 { public string Code { get; set; } public abstract EmployeeType Type { get; } //<--2 } public class Manager : Employee { public override EmployeeType Type { get { EmployeeType.Manager; } }//<--3 public void Manage() { // Managing } } public class Secretary : Employee { public override EmployeeType Type { get { EmployeeType.Secretary; } }//<--4 public void SetMeeting() { // Setting meeting } }

Entonces una fábrica de eso ..

EmployeeFactory.Create(employeeType);

He hecho "4" cambios en su código original y ahora parece un poco "Patrón de fábrica". Puede ser autor del código entender el "patrón de fábrica" ​​mal. pero quién sabe.?