В C # или ООП должны ли 2 класса ссылаться друг на друга, которые связаны между собой? - PullRequest
15 голосов
/ 04 марта 2010

Я работаю над библиотекой классов, используя 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;
    }
}

Проблема у меня возникает, когда я пытаюсь использовать метод в PolicyTerm или Billing, которому нужны данные из содержащего класса. В приведенном выше примере это будет метод «doSomething», пытающийся использовать значение из PolicyTerm, например, дату вступления в силу термина при запросе или сохранении данных в нашей базе данных.

Мне интересно, есть ли у меня правильный дизайн для моих классов из-за этого сценария. Должен ли я просто добавить ссылку на «родительский» класс в дочерних классах, чтобы сделать доступными данные родителя? Или мне нужно переосмыслить общую структуру и дизайн кода?

Мне кажется, что дизайн класса хорошо работает для моделирования данных и наших бизнес-правил, но он создает некоторые ограничения, подобные описанной выше ситуации. Мне понравилось разделение PolicyTerm и Billing за возможность разрешать этот код для изменения и тестирования независимо. Кроме того, я чувствую, что каждый раздел становится меньше и проще.

Любой совет, который может быть предоставлен, будет высоко оценен.

Обновление: блок кода был обновлен, чтобы предоставить более подробную информацию о рассматриваемом коде.

Ответы [ 6 ]

6 голосов
/ 04 марта 2010

Если doSomething() всегда нужна ссылка на родительский объект C, вам действительно следует поместить эту ссылку в C, где вы можете убедиться, что она ссылается на правильный экземпляр B.OTOH, если эта ссылка не всегда является родительской, но, тем не менее, она всегда будет ссылаться на один и тот же экземпляр B, она все же предлагает превратить его в член C.OTOH, если doSomething() можно вызывать с различными ссылками, эту ссылку следует сохранить в качестве параметра метода.

Неплохо само по себе помещать ссылку от дочернего элемента к родителю или иметь взаимную зависимостьдва класса - это зависит от контекста.Следствием этого является то, что эти два класса не могут использоваться по отдельности, поэтому фактически они образуют компонент .Это может быть или не быть приемлемым для вас.

Компоненты в общем случае могут состоять из нескольких классов - коллекция с ее элементами и итератором (ами) фактически является типичным примером.Однако желательно также выразить логическую зависимость между этими классами на физическом уровне, например, сделав один класс внутренним классом другого или сделав оба класса внутренними классами в третьем классе.

2 голосов
/ 04 марта 2010

Два класса не должны, но два интерфейса в порядке.

Конечно, чем меньше интерфейсы, тем лучше. Вы обнаружите, что если интерфейсы достаточно малы (какими они должны быть - см. Принцип разделения интерфейсов ), вам на самом деле не понадобится 2 таких же.

2 голосов
/ 04 марта 2010

Это действительно зависит от ситуации. В общем, если нет четкой, очевидной связи между классами "B" и "C", это красный флаг, который C.doSomething() потребует доступа к B, поскольку C * содержится в B. ..

Однако метод в B, требующий доступа к C, имеет смысл, поскольку C является членом в B.

Как говорится, бывают случаи, когда это уместно. Не зная ваших реальных классов и того, что они представляют, трудно сказать больше ...

0 голосов
/ 02 ноября 2011

По моему мнению, ваше моделирование выглядит немного искаженным, т. Е. Почему в полисе есть свойство типа person и почему есть список конкретной реализации в Policy, т. Е. PolicyTerm. Это соединяет классы вместе и не чувствует себя хорошо - то есть политика имеет клиента? Должен быть клиент имеет политику

Могу ли я предложить следующее (быстро смоделировано и не проверено, но вы сможете увидеть, к чему я клоню)

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;
     }
}
0 голосов
/ 04 марта 2010

Общее правило - держать данные как можно ближе к функциям / методам / классам, которые будут их использовать. Это будет разъединять вещи, и вам не нужно, чтобы оба класса ссылались друг на друга, что фактически заставляет вас создавать дополнительный объект, который может не понадобиться.

И, как сказал ChaosPandion: пожалуйста, опубликуйте более конкретный код, чтобы мы могли лучше вам помочь.

Edit:

Если вы B ссылаетесь на C и C ссылаетесь на B, то вы можете рассмотреть возможность объединения этих двух элементов в один объект. Это работает лучше всего, если два класса не полностью различны. Если нет реальной различимой разницы, просто сложите ее в один класс ..., что может упростить все это.

0 голосов
/ 04 марта 2010

Создание ссылки на требуемый класс совсем не кажется плохой идеей. Если это необходимо, вы можете заставить конструктор класса C взять ссылку на класс B и сохранить ее в переменной-члене.

Сейчас я работаю над проектом с парой классов, которые ведут себя следующим образом.

Другой вариант, который может быть немного более «нормальным», - это иметь событие в классе C, это что-то вроде «SuchAndSuchDataRequired». Класс B может затем прослушивать это событие, когда он получает экземпляр C. Класс C запускает событие из doSomething (), когда ему нужны данные из B, B затем возвращает данные в свой обработчик событий, и бинго-класс C имеет данные и даже не знают, что они пришли из класса B.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...