在C#或OOP中,2个类应该相互引用吗?

时间:2022-01-16 11:58:43

I am working on a class library using C#. I have designed 3 main classes to help model our data. They are designed such that class A contains a list of class B instances, and class B contains a reference to a class C instance, ie:

我正在使用C#编写类库。我设计了3个主要类来帮助建模我们的数据。它们的设计使得类A包含类B实例的列表,而类B包含对类C实例的引用,即:

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;
    }
}

The problem I have is when I try to use a method within PolicyTerm or Billing that needs data from the containing class. In the example above, this would be the method "doSomething" trying to use a value from PolicyTerm, like the effective date for the term in requesting or saving data to our database.

我遇到的问题是当我尝试在PolicyTerm或Billing中使用需要来自包含类的数据的方法时。在上面的示例中,这将是尝试使用PolicyTerm中的值的方法“doSomething”,例如请求或将数据保存到数据库中的术语的生效日期。

I am wondering if I have the correct design for my classes because of this scenario. Should I just add a reference to the "parent" class within the child classes, in order to make the parent's data available? Or do I need to rethink the overall structure and design of the code?

我想知道我是否因为这种情况而为我的班级设计了正确的设计。我是否应该在子类中添加对“父”类的引用,以使父数据可用?或者我是否需要重新考虑代码的整体结构和设计?

I feel like the class design works well for modeling the data and our business rules, but it does create a few limitations like the above situation. I liked the separation of the PolicyTerm and Billing for the ability to allow that code to be modified and tested independently. Also, I feel like it keeps each section smaller and simpler.

我觉得类设计适用于数据建模和业务规则建模,但它确实会产生一些限制,如上述情况。我喜欢将PolicyTerm和Billing分开,以便能够独立修改和测试代码。此外,我觉得它使每个部分更小,更简单。

Any advice that can be provided would be much appreciated.

任何可以提供的建议将不胜感激。

Update: Code block was updated to provide more details on the code in question.

更新:代码块已更新,以提供有关代码的更多详细信息。

6 个解决方案

#1


6  

If doSomething() always needs the reference to the C object's parent, you should indeed put this reference into C where you can ensure that it refers to the correct B instance. OTOH if that reference is not always the parent, but still it is always going to refer to the same B instance, it still suggests turning it into a member of C. OTOH if doSomething() can be called with varying references, that reference should be kept as a method parameter.

如果doSomething()总是需要引用C对象的父对象,那么确实应该将此引用放入C中,以确保它引用正确的B实例。 OTOH如果该引用并不总是父引用,但仍然总是引用相同的B实例,它仍然建议将其转换为C. OTOH的成员,如果可以使用不同的引用调用doSomething(),那引用应该保留为方法参数。

It is not bad per se to put a reference from child to parent, or to have a mutual dependency between two classes - it depends on the context. The consequence of this is that the two classes can not be used separately, so in fact they form a component. This may or may not be acceptable for you.

将一个引用从子级转换为父级或者在两个类之间具有相互依赖性本身并不坏 - 这取决于上下文。这样做的结果是两个类不能单独使用,因此实际上它们构成了一个组件。这可能是也可能不适合您。

Components in general can consist of multiple classes - a collection with its items and iterator(s) is in fact a typical example. However, it is advisable to express the logical dependency between these classes on the physical level as well, e.g. by making one class an inner class of the other, or making both classes inner classes in a third class.

组件通常可以由多个类组成 - 具有项的集合和迭代器实际上是典型的示例。但是,建议在物理层面上表达这些类之间的逻辑依赖关系,例如:通过使一个类成为另一个类的内部类,或者使两个类成为第三类中的内部类。

#2


2  

This really depends on the situation. In general, unless there is a clear, obvious relationship between classes "B" and "C", it's a red flag that C.doSomething() would require access to B, since C is contained within B...

这实际上取决于具体情况。一般来说,除非类“B”和“C”之间存在明显,明显的关系,否则C.doSomething()需要访问B才是红旗,因为C包含在B中......

However, a method in B requiring access to C makes sense, since C is a member within B.

但是,B中需要访问C的方法才有意义,因为C是B中的成员。

That being said, there are times that this is appropriate. Without knowing your actual classes, and what they represent, its difficult to say more...

话虽如此,有时候这是合适的。如果不知道你的实际课程,以及他们所代表的内容,很难说更多......

#3


1  

Two classes shouldn't, but two interfaces is OK.

两个类不应该,但两个接口是可以的。

Of course, the smaller the interfaces the better. You'll find that if the interfaces are small enough (which they should be - see Interface Segregation Principal), you won't actually need 2 of the same.

当然,界面越小越好。你会发现,如果接口足够小(它们应该是 - 参见Interface Segregation Principal),你实际上不需要2个相同的接口。

#4


0  

Creating a reference to your required class doesn't seem a bad idea at all. If it's required, you could make Class C's constructor take the reference to Class B and store it in a member variable.

创建对所需类的引用根本不是一个坏主意。如果需要,可以使C类的构造函数引用B类并将其存储在成员变量中。

I'm working on a project at the moment with a couple of classes behave like this.

我正在研究一个项目,其中有几个类表现得像这样。

Another option which might be a bit more "sane" is to have an event on class C, that's something like "SuchAndSuchDataRequired." Class B could then listen to that event when it gets the instance of C. Class C fires the event from within doSomething() when it needs the data from B, B then returns the data in it's event handler and bingo - class C has the data and doesn't even know it came from class B.

另一个可能更“理智”的选择是在C类上有一个事件,就像“SuchAndSuchDataRequired”。然后B类可以在获取C实例时监听该事件。当C类需要来自B,B的数据时,类C从doSomething()内部触发事件然后返回其中的数据事件处理程序和bingo - 类C具有数据,甚至不知道它来自B类。

#5


0  

The general rule of thumb is keep the data as close as possible to the functions/methods/classes that will be using it. This will keep things decoupled and you won't have to have both classes referencing each other, which actually makes you have to create an extra object that might not be necessary.

一般的经验法则是使数据尽可能接近将要使用它的函数/方法/类。这将使事物分离,并且您不必让两个类彼此引用,这实际上使您必须创建可能不必要的额外对象。

And like ChaosPandion said: please post some more specific code so we can better help you.

和ChaosPandion说:请发布一些更具体的代码,以便我们更好地帮助您。

Edit:

编辑:

If you B references C and C references B, then you might want to consider putting the two together as one object. This works best if the two classes are not completely different. If there is no real distinguishable difference, then just put it together in one class ... that could simplify the whole thing.

如果B引用C和C引用B,那么您可能需要考虑将两者放在一起作为一个对象。如果两个类没有完全不同,这种方法效果最好。如果没有真正可区分的差异,那么只需将它放在一个类中......这可以简化整个事情。

#6


0  

In my opinion your modelling seems a bit skewed i.e. why is there a property of type person within policy and why have a List of a concrete implementation in Policy i.e. PolicyTerm. This couples the classes together and doesn't feel right - i.e. Policy HAS A customer? Should be Customer HAS A Policy

在我看来,你的建模似乎有点偏斜,即为什么在策略中存在类型的属性,为什么在策略中有一个具体实现的列表,即PolicyTerm。这把课程结合在一起并且感觉不对 - 即政策有客户吗?客户应该是一个政策

Can I suggest the following (quickly modelled and not tested but you should be able to see what I'm getting at)

我可以建议以下内容(快速建模并且未经过测试,但您应该能够看到我的内容)

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;
     }
}

#1


6  

If doSomething() always needs the reference to the C object's parent, you should indeed put this reference into C where you can ensure that it refers to the correct B instance. OTOH if that reference is not always the parent, but still it is always going to refer to the same B instance, it still suggests turning it into a member of C. OTOH if doSomething() can be called with varying references, that reference should be kept as a method parameter.

如果doSomething()总是需要引用C对象的父对象,那么确实应该将此引用放入C中,以确保它引用正确的B实例。 OTOH如果该引用并不总是父引用,但仍然总是引用相同的B实例,它仍然建议将其转换为C. OTOH的成员,如果可以使用不同的引用调用doSomething(),那引用应该保留为方法参数。

It is not bad per se to put a reference from child to parent, or to have a mutual dependency between two classes - it depends on the context. The consequence of this is that the two classes can not be used separately, so in fact they form a component. This may or may not be acceptable for you.

将一个引用从子级转换为父级或者在两个类之间具有相互依赖性本身并不坏 - 这取决于上下文。这样做的结果是两个类不能单独使用,因此实际上它们构成了一个组件。这可能是也可能不适合您。

Components in general can consist of multiple classes - a collection with its items and iterator(s) is in fact a typical example. However, it is advisable to express the logical dependency between these classes on the physical level as well, e.g. by making one class an inner class of the other, or making both classes inner classes in a third class.

组件通常可以由多个类组成 - 具有项的集合和迭代器实际上是典型的示例。但是,建议在物理层面上表达这些类之间的逻辑依赖关系,例如:通过使一个类成为另一个类的内部类,或者使两个类成为第三类中的内部类。

#2


2  

This really depends on the situation. In general, unless there is a clear, obvious relationship between classes "B" and "C", it's a red flag that C.doSomething() would require access to B, since C is contained within B...

这实际上取决于具体情况。一般来说,除非类“B”和“C”之间存在明显,明显的关系,否则C.doSomething()需要访问B才是红旗,因为C包含在B中......

However, a method in B requiring access to C makes sense, since C is a member within B.

但是,B中需要访问C的方法才有意义,因为C是B中的成员。

That being said, there are times that this is appropriate. Without knowing your actual classes, and what they represent, its difficult to say more...

话虽如此,有时候这是合适的。如果不知道你的实际课程,以及他们所代表的内容,很难说更多......

#3


1  

Two classes shouldn't, but two interfaces is OK.

两个类不应该,但两个接口是可以的。

Of course, the smaller the interfaces the better. You'll find that if the interfaces are small enough (which they should be - see Interface Segregation Principal), you won't actually need 2 of the same.

当然,界面越小越好。你会发现,如果接口足够小(它们应该是 - 参见Interface Segregation Principal),你实际上不需要2个相同的接口。

#4


0  

Creating a reference to your required class doesn't seem a bad idea at all. If it's required, you could make Class C's constructor take the reference to Class B and store it in a member variable.

创建对所需类的引用根本不是一个坏主意。如果需要,可以使C类的构造函数引用B类并将其存储在成员变量中。

I'm working on a project at the moment with a couple of classes behave like this.

我正在研究一个项目,其中有几个类表现得像这样。

Another option which might be a bit more "sane" is to have an event on class C, that's something like "SuchAndSuchDataRequired." Class B could then listen to that event when it gets the instance of C. Class C fires the event from within doSomething() when it needs the data from B, B then returns the data in it's event handler and bingo - class C has the data and doesn't even know it came from class B.

另一个可能更“理智”的选择是在C类上有一个事件,就像“SuchAndSuchDataRequired”。然后B类可以在获取C实例时监听该事件。当C类需要来自B,B的数据时,类C从doSomething()内部触发事件然后返回其中的数据事件处理程序和bingo - 类C具有数据,甚至不知道它来自B类。

#5


0  

The general rule of thumb is keep the data as close as possible to the functions/methods/classes that will be using it. This will keep things decoupled and you won't have to have both classes referencing each other, which actually makes you have to create an extra object that might not be necessary.

一般的经验法则是使数据尽可能接近将要使用它的函数/方法/类。这将使事物分离,并且您不必让两个类彼此引用,这实际上使您必须创建可能不必要的额外对象。

And like ChaosPandion said: please post some more specific code so we can better help you.

和ChaosPandion说:请发布一些更具体的代码,以便我们更好地帮助您。

Edit:

编辑:

If you B references C and C references B, then you might want to consider putting the two together as one object. This works best if the two classes are not completely different. If there is no real distinguishable difference, then just put it together in one class ... that could simplify the whole thing.

如果B引用C和C引用B,那么您可能需要考虑将两者放在一起作为一个对象。如果两个类没有完全不同,这种方法效果最好。如果没有真正可区分的差异,那么只需将它放在一个类中......这可以简化整个事情。

#6


0  

In my opinion your modelling seems a bit skewed i.e. why is there a property of type person within policy and why have a List of a concrete implementation in Policy i.e. PolicyTerm. This couples the classes together and doesn't feel right - i.e. Policy HAS A customer? Should be Customer HAS A Policy

在我看来,你的建模似乎有点偏斜,即为什么在策略中存在类型的属性,为什么在策略中有一个具体实现的列表,即PolicyTerm。这把课程结合在一起并且感觉不对 - 即政策有客户吗?客户应该是一个政策

Can I suggest the following (quickly modelled and not tested but you should be able to see what I'm getting at)

我可以建议以下内容(快速建模并且未经过测试,但您应该能够看到我的内容)

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;
     }
}