2010-03-04 11 views
14

Estoy trabajando en una biblioteca de clase usando C#. Diseñé 3 clases principales para ayudar a modelar nuestros datos. Están diseñados de tal manera que la clase A contiene una lista de instancias de la clase B y clase B contiene una referencia a una instancia de clase C, es decir:En C# u OOP, ¿deberían las 2 clases estar relacionadas entre sí?

public class Policy 
{ 
    public List <PolicyTerm> myTerms; 
    person Customer; 
    string PolicyNumber; 
} 

public class PolicyTerm 
{ 
    public Billing myBill; 
    Datetime effectivedate; 
    List <Activities> termActivities; 
    public doAction() 
    { 
      use value from Policy, like PolicyNumber; 
    } 

} 

public class Billing 
{ 
    float remainingBalance; 
    Datetime nextDueDate; 
    public void doSomething() 
    { 
     reference value from PolicyTerm, such as effective date; 
     use value from Policy, such as PolicyNumber; 
    } 
} 

El problema que tengo es cuando intento utilizar un método dentro PolicyTerm o Facturación que necesita datos de la clase contenedora. En el ejemplo anterior, este sería el método "doSomething" que intenta usar un valor de PolicyTerm, como la fecha de vigencia del término al solicitar o guardar datos en nuestra base de datos.

Me pregunto si tengo el diseño correcto para mis clases debido a este escenario. ¿Debo simplemente agregar una referencia a la clase "padre" dentro de las clases secundarias, a fin de que los datos del padre estén disponibles? ¿O es necesario que reconsidere la estructura general y el diseño del código?

Siento que el diseño de la clase funciona bien para modelar los datos y nuestras reglas comerciales, pero crea algunas limitaciones como la situación anterior. Me gustó la separación de PolicyTerm y Billing por la posibilidad de permitir que el código se modifique y pruebe de forma independiente. Además, siento que mantiene cada sección más pequeña y simple.

Cualquier consejo que se pueda proporcionar sería muy apreciado.

Actualización: El bloque de código se actualizó para proporcionar más detalles sobre el código en cuestión.

+3

¿Por qué no bajar de la real código que quieres usar y muéstranos eso? Algo como esto realmente depende de la situación. – ChaosPandion

Respuesta

6

Si doSomething() siempre necesita la referencia al padre del objeto C, debe poner esta referencia en C para asegurarse de que se refiere a la instancia correcta B. OTOH si esa referencia no es siempre la principal, pero igual siempre se referirá a la misma instancia de B, aún sugiere convertirla en miembro de C. OTOH si se puede llamar al doSomething() con referencias variables, esa referencia debe mantenerse como un parámetro de método.

No es malo en sí mismo poner una referencia de hijo a principal, o tener una dependencia mutua entre dos clases: depende del contexto. La consecuencia de esto es que las dos clases no se pueden usar por separado, por lo que de hecho forman un componente . Esto puede o no ser aceptable para usted.

Los componentes en general pueden constar de múltiples clases; una colección con sus elementos e iterador (es) es, de hecho, un ejemplo típico. Sin embargo, es aconsejable expresar la dependencia lógica entre estas clases también en el nivel físico, p. haciendo que una clase sea una clase interna de la otra, o que ambas clases sean internas en una tercera clase.

0

Crear una referencia a la clase requerida no parece ser una mala idea. Si es necesario, puede hacer que el constructor de Clase C tome la referencia a la Clase B y la almacene en una variable miembro.

Estoy trabajando en un proyecto en este momento con un par de clases de comportarse así.

Otra opción que podría ser un poco más "sensata" es tener un evento en la clase C, que es algo así como "SuchAndSuchDataRequired". La clase B podría escuchar ese evento cuando obtenga la instancia de C. La clase C dispara el evento desde dentro de DoSomething() cuando necesita los datos de B, B luego devuelve los datos en su controlador de eventos y bingo - la clase C tiene el datos y ni siquiera sabe que vino de la clase B.

+0

Me gusta la idea de los eventos, pero eso podría ser más complicado de lo necesario. Una vez más, todo depende de qué esté tratando de hacer exactamente. –

0

La regla general es mantener los datos lo más cerca posible de las funciones/métodos/clases que lo usarán. Esto mantendrá las cosas desacopladas y no tendrá que tener ambas clases haciendo referencia entre sí, lo que realmente hace que tenga que crear un objeto adicional que podría no ser necesario.

Y como ChaosPandion dijo: por favor, publique un código más específico para que podamos ayudarlo mejor.

Editar:

Si usted referencias B y C C referencias B, entonces es posible que desee considerar la posibilidad de que los dos juntos como un solo objeto. Esto funciona mejor si las dos clases no son completamente diferentes. Si no hay una diferencia real distinguible, simplemente júntelo en una clase ... eso podría simplificar todo.

+0

El código se actualizó para proporcionar más detalles. Acepto que PolicyTerm (clase B) y Billing (clase C) están estrechamente relacionados entre sí, pero siento que es bueno tenerlos como clases separadas. – Swoop

2

Esto realmente depende de la situación. En general, a menos que exista una relación clara y obvia entre las clases "B" y "C", es una bandera roja que C.doSomething() requeriría acceso a B, ya que C es contenida dentro de B ...

Sin embargo, un método en el B que requiere el acceso a C tiene sentido, ya que C es un miembro dentro de B.

Dicho esto, hay veces que esto es apropiado. Sin conocer sus clases reales, y lo que representan, es difícil decir más ...

+0

La pregunta se actualizó para proporcionar más detalles. Estoy de acuerdo con el comentario de la bandera roja. Si bien puedo hacer que funcione el código, siento que algo no está bien. – Swoop

+0

@Swoop: en su caso, casi parece que la facturación debe ser parte del cliente, y Billing.doSomething debe pasar un PolicyTerm como argumento ... –

1

Dos clases no deberían, pero dos interfaces están bien.

Por supuesto, cuanto más pequeñas sean las interfaces, mejor. Descubrirá que si las interfaces son lo suficientemente pequeñas (lo que deberían ser, consulte Interface Segregation Principal), no necesitará 2 de las mismas.

0

En mi opinión, su modelado parece un poco sesgado, es decir, ¿por qué hay una propiedad de tipo persona dentro de la política y por qué tener una lista de una implementación concreta en la política, es decir, PolicyTerm. Esto une las clases y no se siente bien, es decir, ¿la política TIENE un cliente? En caso de ser cliente tiene una política

¿Puedo sugerir lo siguiente (de forma rápida modelada y no probado, pero usted debería ser capaz de ver lo que quiero llegar)

public class Customer() 
{ 
    prop name,etc,etc; 
    public List<IPolicyTerm> Policies{get;set;}//I use public getters and setters throughout but you need to choose what level of encapsulation you want 
    private Account customerAccount{get;set}   

    public Customer() 
    { 
     //ctor 
     customerAccount = doDbCall; 
     Policies = doDbCall; 
    } 

    public decimal GetCurrentPolicyCost() 
    { 
     decimal cost = 0; 
     foreach(var policy in Policies) 
     { 
      if(policy.DueDate < DateTime.Now){ 
      cost += policy.GetCost(); //for example but you can call whatever is defined at the interface level 
      } 
     } 
     return cost; 
    } 

    public bool HasEnoughFunds() 
    { 
     return customerAccount.Balance >= GetCurrentPolicyCost(); 
    } 

    //keeping Account hidden in Person as Person has a reference to Account. 
    //By doing so there is type coupling between the two classes 
    //BUT you can still modify Policies away from Person 
    private class Account 
    { 
     //should only contain properties and I assume only one 'Account' per person 
    } 
} 

public interface IPolicyTerm 
{ 
    object Id{get;set} 
    DateTime DueDate {get;set;} 
    decimal GetCost(); 
} 

///now we can have polymorphic Policies i.e. the cost of one can be calculated differently based on policy 

public class LifeCoverPolicy : IPolicyTerm 
{ 

    public object Id; 
    public DateTime DueDate{get;set;}   

    public decimal GetCost() 
    { 
      return 10; 
    } 
} 
Cuestiones relacionadas