2009-03-04 1 views
0

У меня есть класс, к которому я постоянно добавляю.Создание класса следует за OCP - Факторинг в объектах

public class OrderRepository{ 
    public void Add(IEnumerable<Order> orders){} 
    public void Update(IEnumerable<Order> orders){} 
    public void Remove(IEnumerable<Order> orders){} 
    public void Expedite(IEnumerable<Order> orders){} 
    public void GetOrderData(Order order, DateTime start, DateTime end) 
    etc... 
} 

Мне пришло в голову, что этот класс не является открытым Closed, из-за всех этих новых возможностей, добавленных. Поэтому я решил закрыть этот класс против этого изменения, инкапсулируя эти функции в объекты Request. В итоге я получаю что-то вроде:

public abstract class RequestBase{} 

public class AddRequest : RequestBase{} 

etc... 

public class OrderRepository{ 
    public void ProcessRequest(RequestBase request){} 
} 

Который делает OrderRepository открытым для расширения и закрыт для модификации. Тем не менее, я быстро столкнулся с несколькими проблемами с этим:

1.) Данные, на которые должен работать данный запрос, предоставляются как пользователем, так и временем выполнения. Я, очевидно, не могу удовлетворить как с одним конструктором. Я не могу:

public class AddRequest{ 
    public AddRequest(IEnumerable<Order> orders, int UserSuppliedContextArg1, DependencyInjectionArg1, DependencyInjectionArg2); 
} 

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

2.) Следующее, о чем я думал, это разделить его на 2 отдельных класса. Пользователь создавал и заполнял RequestContext, а затем передавал это в репозиторий, что создавало бы RequestProcessor (не может думать о более лучшем имени). Я думал о том, чтобы сделать:

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

public class RequestProcessorBase<TRequestContext, TRequestProcessorBase> where TRequestContext : RequestContextBase<TRequestProcessorBase> 

Это странно, и я обычно не любят curiously recurring template pattern. Кроме того, идея пользователя, заполняющего контекст и предлагающего мне сделать запрос, кажется странным, хотя это может быть просто проблема с именами.

3.) Я думал о том, чтобы избавиться от всего вышеперечисленного и просто иметь:

public AddRequest{ 
    public AddRequest(DependencyInjectionArg1, DependencyInjectionArg2, ...){} 

    public void PackArgs(UserSuppliedContextArg1, UserSuppliedContextArg2, UserSuppliedContextArg3, ...){} 
} 

что не плохо, но API некрасиво. Теперь клиенты этого объекта должны «сконструировать» его дважды, как это было. И если они забывают вызвать PackArgs, я должен бросить какое-то исключение.

Я мог бы продолжать, но это самые запутанные проблемы, которые у меня есть на данный момент. Есть идеи?

ответ

0

Айенде имеет few posts на this subject.

В основном, что вы хотите сделать, это отделить ваш запрос от вашего репозитория и превратить ваш запрос в нечто, что вы составите. С запросом, созданным композицией, вы можете легко расширить его, чтобы добавить новые способы запроса без необходимости добавления новых методов в ваш репозиторий. Вы можете в конечном итоге с чем-то вроде этого:

public class Repository<T> 
{ 
    T Find(IQueryCriteria queryCriteria); 
} 

Я на самом деле не сделал это в NHibernate, но мы сделали это с LLBLGenPro, и она работала очень хорошо.Мы использовали свободно интерфейс для нашего запроса объектов таким образом, мы могли бы написать критерии запроса, как это:

var query = new EmployeeQuery() 
    .WithLastName("Holmes") 
    .And() 
    .InDepartment("Information Systems"); 

var employee = repository.Find(query); 

Расширяя возможности репозитория затем составили просто добавляя новые методы объекта запроса.

+0

Это выглядит несколько полезным, но мой вопрос касается инкапсуляции запросов, а не запросов. Кроме того, я не получаю доступ к БД за репозиторием, хотя это, вероятно, не имеет значения. – DavidN

1

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

Если класс следует за SRP, «постоянное добавление к нему» нарушает это по определению. Это указывает на то, что операции, которые вы вводите, могут быть лучше адресованы службами или в противном случае должны быть отделены от репозитория.

Редактировать в ответ на комментарий

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

Отмена операций из хранилища будет первым шагом. Вы могли бы сделать что-то вроде этого:

public interface IOrderExpeditionService 
{ 
    void Expedite(IEnumerable<Order> orders); 
} 

public interface IOrderDataService 
{ 
    void GetOrderData(Order order, DateTime start, DateTime end); 
} 
+0

«Это указывает на то, что операции, которые вы вводите, могут быть лучше адресованы службами или в противном случае должны быть отделены от репозитория». - Это то, что я пытался сделать. Есть ли у вас предложения по развязке? – DavidN

+0

Отличный ответ. Однако я бы использовал эти сервисы как классы. Это намного проще, и все еще легко выполнить их тестирование и даже классы, которые зависят от них. –

+0

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

 Смежные вопросы

  • Нет связанных вопросов^_^