Как применить внедрение зависимостей к абстрактной фабрике - PullRequest
5 голосов
/ 02 февраля 2012

Я только что закончил книгу Марка Симанна Внедрение зависимостей в .NET , и сейчас я пытаюсь реорганизовать какой-то устаревший код. (На данном этапе я не полагаюсь на какой-либо конкретный DI-контейнер, а просто пытаюсь переместить все зависимости в одно место).

Я смотрю на следующий фабричный класс, который определяет ArchiveType, читая первые несколько байтов архива с archiveReader.GetArchiveType(), а затем возвращает экземпляр ArchiveRestorer на основе перечисления ArchiveType.

public class ArchiveRestorerFactory : IArchiveRestorerFactory
{
    public ArchiveRestorer Create(ArchiveReader archiveReader)
    {
        ArchiveType type = archiveReader.GetArchiveType();
        switch (type)
        {
            case ArchiveType.CurrentData:
                return new CurrentDataArchiveRestorer(archiveReader);
                break;
            case ArchiveType.HistoricalData:
                return new HistoricalDataArchiveRestorer(archiveReader);
                break;
            case ArchiveType.AuditTrail:
                return new AuditTrailArchiveRestorer(archiveReader);
                break;
            default:
                throw new Exception("ArchiveRestorerFactory error: Unknown value for ArchiveType.");
        }
    }
}

Как мне провести рефакторинг, чтобы класс не зависел от конкретных типов CurrentDataArchiveRestorer, HistoricalDataArchiveRestorer и AuditTrailArchiveRestorer?

Должен ли я переместить три бетоноделителя в конструктор фабрики?

public ArchiveRestorer Create(ArchiveReader archiveReader, 
    ArchiveRestorer currentDataArchiveRestorer, 
    ArchiveRestorer historicalDataArchiveRestorer, 
    ArchiveRestorer auditTrailDataArchiveRestorer)
{
    // guard clauses...
    // assign to readonly fields
}

Это, кажется, подход, предложенный здесь , но тогда он будет создавать экземпляры всех трех реставраторов, когда нужен только один? Что если бы у меня было 20 различных возможных конкретных реализаций вместо этого?

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

Каков наилучший способ рефакторинга?

Ответы [ 5 ]

2 голосов
/ 03 февраля 2012

Почему бы не сделать ArchiveReader ответственным за создание соответствующего ArchiveRestorer?Тогда первая итерация кода будет выглядеть так:

public class ArchiveRestorerFactory : IArchiveRestorerFactory
{
    public ArchiveRestorer Create(ArchiveReader archiveReader)
    {
        ArchiveRestorer restorer = archiveReader.GetArchiveRestorer();
        return restorer;
    }
}

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

2 голосов
/ 02 февраля 2012

Я бы сделал это, учитывая код, который вы уже получили, - создать фабрику для каждого из этих объектов, которая имеет метод Create().

У меня также будет интерфейс для этих фабрик, и они будут наследоваться от общего интерфейса фабрики.

Затем вы можете использовать интерфейсы как точку для внедрения в ваш конструктор.

Что можно назвать похожим на это:

case ArchiveType.CurrentData:
                return _currentDateArchiveRestorerFactory.Create(archiveReader);
                break;

В качестве альтернативы может быть лучше иметь одну фабрику, которая создает экземпляр данного типа. Поскольку все эти объекты являются реставраторами, вы можете просто создать экземпляр на основе enum, а не switch.

_restorerFactory.Create(ArchiveType.CurrentData);
1 голос
/ 02 февраля 2012

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

0 голосов
/ 02 июля 2015

Я бы решил эту проблему, согласившись на соглашение об именах и используя способность Unity называть регистрации. Пример этого здесь: https://dannyvanderkraan.wordpress.com/2015/06/29/real-world-example-of-dependency-injection-based-on-run-time-values/

0 голосов
/ 02 февраля 2012
interface ILogger
{
    void Log(string data);
}

class Logger : ILogger
{
    .
    .
    .
}

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

class MyComponent
{
  void DoSomeWork()
  {
    // Get an instance of the logger
    ILogger logger = Helpers.GetLogger();
    // Get data to log
    string data = GetData();

    // Log
    logger.Log(data);
  }
}

class Helpers
{
  public static ILogger GetLogger()
  {
    // Here, use any sophisticated logic you like
    // to determine the right logger to instantiate.

    ILogger logger = null;
    if (UseDatabaseLogger)
    {
        logger = new DatabaseLogger();
    }
    else
    {
        logger = new FileLogger();
    }
    return logger;
  }
}
class FileLogger : ILogger
{
    .
    .
    .
}

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