архитектура с инверсией зависимостей (до DI Framework) - PullRequest
2 голосов
/ 30 марта 2020

Я изучаю DI. У меня есть некоторые проблемы с архитектурой, может быть, я упускаю некоторые моменты.

Предположим, у меня есть не-DI-код (я читаю список "Person" из файла )

static void Main()
{
   PersonReaderFromFile personReader = new PersonReaderFromFile("path-to-file");
   Person p = personReader.GetNext();  //return a Person parsed from file line or NULL at EOF
}

class Person 
{
    //stuffs here
}

class PersonReaderFromFile
{
    public Person GetNext()
    {
        Person person = new Person();
        //some logic
        return person;
    }
}

Во-первых, для реализации шаблонов DI мне нужны интерфейсы, поэтому я делаю

static void Main()
{
   iPersonReader personReader = new PersonReaderFromFile("path-to-file");
   iPerson p = personReader.GetNext();
}

interface iPerson
{
}

class Person : iPerson
{
}

interface iPersonReader
{
    iPerson GetNext();
}

class PersonReaderFromFile : iPersonReader
{
    public iPerson GetNext()
    {
        Person person = new Person();
        //some logic
        return person;
    }
}

А теперь моя проблема: PersonReaderFromFile зависит от реализации Person. Все нормально? Или мне нужен дополнительный класс, например PersonFactory?

static void Main()
{
    iPersonFactory factory = new PersonFactory();
    iPersonReader personReader = new PersonReaderFromFile("path-to-file", factory);
    iPerson person = personReader.GetNext();
}

interface iPerson
{
}

class Person : iPerson
{
}

interface iPersonReader
{
    iPerson GetNext();
}

class PersonReaderFromFile : iPersonReader
{
    iPersonFactory _factory;

    public PersonReaderFromFile(iPersonFactory factory)
    {
        _factory = factory;
    }

    public iPerson GetNext()
    {
        Person person = _factory.CreateNewPerson();
        //some logic
        return person;
    }
}

interface iPersonFactory 
{
    iPerson CreateNewPerson();
}

class PersonFactory : iPersonFactory
{
    iPerson CreateNewPerson()
    {
        Person person = new Person();
        return person;
    }
}

Теперь PersonFactory зависит от реализации Person, но он должен быть правильным. Любой совет? Tnx для всех.

РЕДАКТИРОВАТЬ: я показываю пример реализации PersonReaderFromFile

class PersonReaderFromFile
{
    StreamReader _fileReader;

    public PersonReaderFromFile(string path)
    {
        _fileReader = new StreamReader(file);
    }

    public Person GetNext()
    {
        string line = _fileReader.ReadLine();
        if (line == null)
        {
                _fileReader.Close();
                return null;
        }

       string name, lastName, email;
       ParseInformationFromLine(line, out name, out lastName, out email);

       Person p = new Person { Name = name, LastName = lastName, Email = email };

       return p;
    }

   private ParseInformationFromLine(string line, out string name, out string lastName, out string email)
   {
        //I don't think that matters
   }
}

1 Ответ

1 голос
/ 30 марта 2020

PersonReaderFromFile не зависит от Person. Этот класс выглядит просто как POCO, который представляет данные времени выполнения.

PersonReaderFromFile зависит от StreamReader вместе с ParseInformationFromLine функцией

Сначала абстрагируйте эти детали реализации в свои собственные проблемы.

public interface IReadLines {
    string ReadLine();
}

public interface IParsePersonInformationFromLine {
    void ParseInformationFromLine(string line, out string name, out string lastName, out string email);
}

Целевой класс будет явно зависеть от абстракций

public class PersonReaderFromFile {
    private readonly IReadLines reader;
    private readonly IParsePersonInformationFromLine parser;

    public PersonReaderFromFile(IReadLines reader, IParsePersonInformationFromLine parser) {
        this.reader = reader;
        this.parser = parser;
    }

    public Person GetNext() {
        string line = reader.ReadLine();
        if (line == null) {
            return null;
        }

        string name, lastName, email;
        parser.ParseInformationFromLine(line, out name, out lastName, out email);

        Person p = new Person { Name = name, LastName = lastName, Email = email };

        return p;
    }
}

Отдельные абстракции будут иметь свои собственные реализации для удовлетворения желаемой функциональности, которая будет использоваться во время выполнения. Например, читатель внутренне все еще будет использовать StreamReader для получения строк. PersonReaderFromFile не нужно ничего знать о том, как извлекается строка. Только то, что ему нужна строка при вызове.

Main теперь можно реорганизовать для использования Pure DI, например

static void Main() {
    IReadLines reader = new ReadLinesImplementation("path-to-file");
    IParsePersonInformationFromLine parser = new ParsePersonInformationFromLine(); 
    PersonReaderFromFile personReader = new PersonReaderFromFile(reader, parser);
    Person p = personReader.GetNext();  //return a Person parsed from file line or NULL at EOF
}

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

...