Разделение класса, который зависит от другого класса, конструктор которого принимает аргумент - PullRequest
3 голосов
/ 09 июля 2019

Я практиковался в написании чистого кода с использованием SOLID.Я также использовал DI в своем коде для удаления связи, но только через конструктор.Во многих случаях я использовал DI, я использовал его только для вызова методов.Чего я пока не понимаю, так это как отделить, когда у вас есть зависимый класс, конструктор которого принимает аргумент внутри другого класса.Если var obj = new A(month) создает зависимость и тесную связь внутри класса B, как мне отделить / абстрагировать это?Это где интерфейс со свойством входит?Если так, как я могу использовать это здесь?

public class A 
{
    private string _month;
    public A(string month) 
    {
        _month = month;
    }
}

public class B 
{
    public List<A> ListOfMonths;
    public B() 
    {
        ListOfMonths = new List<A>();
    }

    public List<A> SomeMethod() 
    {
        string[] months = new [] 
        {
            "Jan",
            "Feb",
            "Mar"
        };

        foreach(var month in months) 
        {
            var obj = new A(month); // If this is coupling, how do I remove it?
            ListOfMonths.Add(obj)
        }

        return ListOfMonths;
    }
}

Ответы [ 3 ]

4 голосов
/ 09 июля 2019

Если вы хотите отделить, вам нужно удалить любую ссылку на A из B и заменить их на IA (интерфейс, аналогичный A), который является заполнителем для любого класса, который заменит A.

ВЗатем конструктор B предоставляет фабрику, способную создавать экземпляры IA.Вы идете дальше, размещая абстрактную фабрику, что означает, что вы предоставляете интерфейс фабрики, способной создавать экземпляры IA.

Вот пример, основанный на вашем коде:

    public interface IA
    {
    }

    public interface IAFactory
    {
        IA BuildInstance(string month);
    }

    public class AFactory : IAFactory
    {
        public IA BuildInstance(string month)
        {
            return new A(month);
        }
    }

    public class A : IA
    {
        public A(string month)
        {
        }
    }

    public class B
    {
        private readonly IAFactory factory;
        public List<IA> ListOfMonths;

        public B(IAFactory factory)
        {
            this.factory = factory;
            ListOfMonths = new List<IA>();
        }

        public List<IA> SomeMethod()
        {
            string[] months = new[] {"Jan", "Feb", "Mar"};
            foreach (var month in months)
            {
                var obj = factory.BuildInstance(month);
                ListOfMonths.Add(obj);
            }

            return ListOfMonths;
        }
    }
3 голосов
/ 09 июля 2019

TL; DR - Нет очевидной связи, которую нужно исправить. Зависимость от абстракций велика, но можно увлекаться и добавлять их там, где они нам не нужны. Создавайте абстракции с точки зрения других классов, которые будут зависеть от них , когда вы знаете, что они вам нужны , так что вы всегда пишете нужный код, а не код, который вам не нужен.


Для вашего класса создание экземпляров A не плохая связь. Создание экземпляров A является очевидной целью B. B не зависит от A в том смысле, что он никоим образом не использует его. Он просто создает A и возвращает его, потому что это то, что он должен делать.

Другой способ взглянуть на это - как метод, который возвращает A, не может быть каким-либо образом связан с A? B не нужен завод. Это это фабрика.

Нет также очевидной причины, по которой вам нужна абстракция для представления A. Если вам нужно было смоделировать A, вы можете определить интерфейс как IA. Но ничто, показанное здесь, не означает, что вам это нужно. Создать «настоящий» экземпляр A так просто, что нет необходимости его высмеивать.

var a = new A("February");. Что издеваться?

И если вам нужна абстракция для A (например, IA), то единственное изменение, которое необходимо немедленно сделать в B, это изменить SomeMethod, чтобы вернуть List<IA> вместо List<A>. В рамках этого метода вы создадите List<IA> вместо List<A>, но вы все равно будете заполнять его конкретными экземплярами A.

Связывание, которое будет удалено (которое, как я подчеркнул, не показано в вашем коде), будет с другими классами, которые не показаны, которые в настоящее время зависят от A и зависят от B для обеспечения Это. Теперь они будут зависеть от B для предоставления IA, поэтому эти другие классы больше не будут связаны с A.

Абстракция великолепна, но она может стать кроличьей ношей, где мы начнем добавлять интерфейсы и фабрики, которые нам на самом деле не нужны. (Что происходит, когда вы закончите замену A на IA? Теперь вы подключены к List<T> - что с этим делать?) Если вы не можете протестировать один класс без проверки его зависимостей, это хороший признак того, что зависимость должна быть абстракцией, над которой можно издеваться.

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


Вот другой способ взглянуть на это: если вам нужна абстракция, скорее всего, это будет абстракция к , представляющему B. Если другие классы, не показанные здесь, нуждаются в экземплярах A или IA, и логика их создания была бы немного более сложной, то эти классы могли бы зависеть от фабрики, например

interface ISomethingFactory
{
    List<IA> Create();
}

B будет реализацией этой фабрики.

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

1 голос
/ 09 июля 2019

Один из способов отделить B от A - определить контракт для A

interface IA { }

и реализовать его с A

public class A : IA {
 private string _month;
 public A(string month) {
  _month = month;
 }
}

Затем из B замените всю ссылку на A на IA

. Для разрешения реализации можно использовать фабрику зависимостей

public class AFactory {
 public IA CreateIA(string month) {
  return new A(month);
 }
}

. AFavtory может быть создан при помощи B, когда это необходимо(в SomeMethod)

Разделение может быть переведено на другой уровень с определением контракта для фабричного класса

interface IAFactory {
 IA CreateIA(string month);
}

И реализовать этот контракт с AFactory.

После этого экземпляр IAFactory может быть введен в B с помощью конструктора

public B(IAFactory aFactory){
 _aFactory = aFactory;
 ...
}
...