2017-02-14 25 views
2

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

Одним из примеров является ввод в него IRepository, который содержит доступ к репозиторию данных. И IRepository содержит DatabaseContext, который записывает в базу данных через повторно IDbContext, который вводится.

SoftwareSession - единственная внедренная общая инфраструктура для доступа к базе данных, действующей в качестве шлюза. Это означает, что когда я хочу написать объект в базу данных, например WriteCar, мне нужно будет реализовать 3 интерфейса, 2 функции делегирования составленным объектам и 1 функцию с реализацией. Это поясняется в фрагменте кода ниже. Подписи WriteCar определяются одинаково в трех интерфейсах (IRepository, ISoftwareSession, IDbContext), 2 места, где он не реализован (репозиторий, SoftwareSession), который просто вызывает связанные функции связанных объектов и 1 место фактической реализации (IDbContext)

Это означает, что когда я хочу реорганизовать, переместить код, добавить функциональность или изменить подписи функций, мне всегда придется менять 6 мест для одной функции.

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

Что представляет собой архитектурно более удобный способ реализации этого? Может быть, даже используя какой-то умный способ лямбда или делегатов уменьшить количество кода, написанного для каждой новой функции? Или даже некоторые библиотеки (например, automapper упрощают DTO) или инструменты для облегчения генерации этого кода из какого-то механизма шаблонов с помощью Visual Studio, Resharper и т. Д.?

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

public class SoftwareSession : ISoftwareSession 
{ 
    ... 
    IRepository repository; 
    public void WriteCar(Car car){ 
     repository.WriteCar(car); 
    } 
    ... 
} 

public interface ISoftwareSession{ 
    ... 
    void WriteCar(Car car); 
    ... 
} 


public class Repository : IRepository{ 
    ... 
    IDbContext context; 
    public void WriteCar(Car car){ 
     context.WriteCar(car); 
    } 
    ...   
} 

public interface IRepository{ 
    ... 
    void WriteCar(Car car); 
    ... 
} 

public class MyDbContext : IDbContext{ 
    ... 
    public void WriteCar(Car car){ 
     //The Actual Implementation here. 
     ... 
    } 
    ... 
} 

public interface IDbContext{ 
    ... 
    void WriteCar(Car car); 
    ... 
} 
+0

Интерфейсы может наследовать тоже! Почему бы не использовать интерфейс ICarWriter, а затем «IRepository: ICarWriter», «ISoftwareSession: ICarWriter» и т. Д. –

+0

Я считаю, что проблема заключается в реализации вашей сессии программного обеспечения, это нарушает единую ответственность, поскольку она появляется в вашей структуре, что что-либо происходит с базой данных, writeCar, writeCustomer и т. Д. Должно быть, в этом классе есть acccessor. – Dys1

+0

@ BarryO'Kane Я согласен - это способ рефакторинга интерфейсов - и имеет смысл от уменьшения перспективы дублирования кода. Я готов ждать других возможных способов упрощения этого. – user3141326

ответ

1

С одной стороны, ваш IDbContext и IRepository одинаковы. Вам, вероятно, захочется удалить IDbContext или, по крайней мере, удалить из него методы, объявленные в IRepository.

Тогда как MyDbContext и Repository будут осуществлять IRepository и Repository класса будет просто обертка MyDbContext.

Затем, если Repository только переадресовывает вызовы на MyDbContext, то вам, вероятно, и этот класс не нужен.

Кроме того, я не вижу, что вы делаете что-либо в SoftwareSession, кроме пересылки вызова в содержащийся репозиторий. Вам действительно нужно SoftwareSession, или было бы целесообразно передать IRepository непосредственно тому, кто вызывает объект сеанса?

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

+0

Спасибо Zoran. IRepository добавляется из-за нескольких DbContexts (например, один контекст может быть DrivingContext, где вы смотрите на дорожные знаки, а другой контекст может быть контекстом обслуживания или администрирования, где мы реализуем функции, связанные с обслуживанием). В этом случае репозиторий действует как агрегатор различных контекстов в моей реализации. Если бы я не использовал сеанс программного обеспечения, мне пришлось бы вводить мой репозиторий или DbContexts в каждый другой класс, где вызываются эти функции, не приведет ли это к дополнительной репликации и также нарушит инкапсуляцию? – user3141326

0

Не видя корни композиции, я не совсем уверен, как работает ваша реализация, но я бы предложил изучить использование контейнера Inversion of Control (IoC). Поскольку ваша реализация ISoftwareSession зависит только от экземпляра IRepository, вам нужно только ввести это в конструктор класса. То же самое касается и вашей реализации IRepository: вам нужно только ввести свой IDbContext в конструктор.

С контейнером IoC вы «регистрируетесь», то есть подключаете свои интерфейсы к своей реализации при запуске приложения (в корне состава), и контейнер заботится о создании необходимых экземпляров при разрешении зависимостей. Тогда все, что вам нужно сделать, это получить экземпляр SoftwareSession из контейнера, и вы уйдете.

Таким образом, вы можете изменить SoftwareSession реализации, как это:

public class SoftwareSession : ISoftwareSession 
{ 
    IRepository repository; 

    public SoftwareSession(IRepository repository) 
    { 
     this.repository = repository; 
    } 

    public void WriteCar(Car car) 
    { 
     repository.WriteCar(car); 
    } 
} 

И ваша Repository реализация так:

public class Repository : IRepository 
{ 
    IDbContext context; 

    public Repository(IDbContext dbContext) 
    { 
     context = dbContext; 
    } 

    public void WriteCar(Car car) 
    { 
     context.WriteCar(car); 
    } 
} 

Тогда вот ваш состав корня:

var ioc = new MyIocContainer(); 

// register your interfaces and their associated implementation types with the IoC container 
ioc.Register<ISoftwareSession, SoftwareSession>(); 
ioc.Register<IRepository, Repository>(); 
ioc.Register<IDbContext, MyDbContext>(); 

// resolve the IoC container 
ioc.Resolve(); 

// get your `ISoftwareSession` instance 
var session = ioc.GetConcrete<ISoftwareSession>(); 

var newCar = new Car(); 

session.WriteCar(newCar); 
+0

Рори спасибо за ваш ответ. Я уже использую Ninject с инъекцией конструктора для каждого элемента в вопросе. Проблема заключается не в фактической инъекции, а в использовании интерфейса WriteCar и делегаций реализации в 6 местах на каждую функцию с этой архитектурой. Все мои оставшиеся классы mainline имеют только программную спецификацию, вводимую через конструктор, и они звонят, например. WriteCar на этом сеансе программного обеспечения для вызова. Тем не менее, я должен написать/дублировать подпись 6 раз, чтобы перейти к фактической реализации. – user3141326

+0

Я не понимаю, что вы подразумеваете под «использованием интерфейса WriteCar и делегаций реализации в 6 местах за функцию». Я не вижу интерфейс «WriteCar». Что такое «делегирование реализации»? и где «шесть мест» в функции (методе?), о которой вы говорите? Я не вижу этого в коде, который вы предоставили, поэтому я запутался ... –

+0

Извините, что не уточнил этот Рори, я исправлю его и в вопросе. Подписи WriteCar определены одинаково в трех интерфейсах (IRepository, ISoftwareSession, IDbContext), 2 места, где он не реализован (репозиторий, SoftwareSession), который просто вызывает связанные функции связанных объектов и 1 место фактической реализации (IDbContext) – user3141326