Вопрос о фабрике дизайна архитектуры - PullRequest
2 голосов
/ 12 сентября 2010

Рассмотрим этот пример

Интерфейс

interface IBusinessRules
{
    string Perform();
}

Наследующие

class Client1BusinessRules: IBusinessRules
{
    public string Perform()
    {
        return "Business rule for Client 1 Performed";
    }
}

class Client2BusinessRules: IBusinessRules
{
    public string Perform()
    {
        return "Business rule for Client 2 Performed";
    }
}

class Client3BusinessRules: IBusinessRules
{
    public string Perform()
    {
        return "Business rule for Client 3 Performed";
    }
}

фабричный класс

class BusinessRulesFactory
{
    public IBusinessRules GetObject(int clientIdentityCode)
    {
        IBusinessRules objbase = null;
        switch (clientIdentityCode)
        {
            case 1:
                objbase = new Client1BusinessRules();
                break;
            case 2:
                objbase = new Client2BusinessRules();
                break;
            case 3:
                objbase = new Client3BusinessRules();
                break;
            default:
                throw new Exception("Unknown Object");
        }
        return objbase;
    }
}

пример использования:

class Program
{
    static void Main(string[] args)
    {
        BusinessRulesFactory objfactory = new BusinessRulesFactory ();
        IBusinessRulesFactory objBase = objfactory.GetObject(2);
        Console.WriteLine(objBase.Perform());

        objBase = objfactory.GetObject(3);
        Console.WriteLine(objBase.Perform());
        Console.Read();
    }
}

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

class Client1BusinessRules: IBusinessRules
{
    public string Perform()
    {
        return "Client1 Business rules is Performed";
    }


    public string Calculate()
    {
        return "Additional functionality for CLient1";
    }
}

Как мне предположить, что это называется в пользовательском интерфейсе что-то вроде этого

 objBase = objfactory.GetObject(1);
 Console.WriteLine(objBase.Calculate());

Есть ли другое решение?заранее спасибо

РЕДАКТИРОВАТЬ: я переписываю его, чтобы напоминать мой текущий дизайн проекта

Ответы [ 6 ]

3 голосов
/ 12 сентября 2010

Если вы хотите придерживаться текущей архитектуры, вы можете ввести новое объявление интерфейса

interface ICalculationRules  
{  
    string Calculate();  
}

Теперь давайте изменим Client1BusinessRules, добавив объявление интерфейса:

class Client1BusinessRules: IBusinessRules, ICalculationRules
{
     // everything remains the same  
}

Измените свой код вызова следующим образом:

var objBase = objfactory.GetObject(1);  
Console.WriteLine(objBase.Calculate());    
var calcBase = obj as ICalculationRules;     
if (calcBase != null) calculable.Calculate();     

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

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

3 голосов
/ 12 сентября 2010

Я предполагаю, что вы используете фабричный класс для того, чтобы:

  • имел стандартный фасад, принимающий параметры, которые приводят к выбору бизнес-правил и инициализация
  • инкапсулировать предоставление бизнес-правил
  • отделить пользователей от реальных реализаций IBusinessRules

Следовательно, я бы решил вашу проблему, введя новый интерфейс

interface IComputableRules : IBusinessRules
{
    string Calculate();
}

Покаследуя дизайну на основе интерфейса, нет ничего плохого в приведении фактического экземпляра к интерфейсу, отличному от IBusinessRules.

IBusinessRules businessRule = objFactory.GetObject(...some input...)
...
// check if the computable service is supported and print the result
IComputableRules computable = businessRule as IComputableRules;
if (computable)
{
     Console.WriteLine(computable.Calculate());
}

. Здесь вы можете рассматривать свои классы бизнес-правил как поставщиков услуг, которые гарантируютнекоторые базовые услуги, а также дополнительные дополнительные услуги в зависимости от характера бизнес-правила.

Примечание. Превратив BusinessRulesFactory в общий класс, вы можете сделать указание на конкретную услугу частью заводского контракта.и убедитесь, что возвращенная реализация бизнес-правила будет поддерживать определенный (в противном случае выберитеионный) сервис.

class BusinessRulesFactory<TService> where TService : IBusinessRules
{
     public TService GetObject(int clientIdentityCode)
     {
         // ... choose business rule in respect to both clientIdentityCode and TService
     }
}

В случае, если вам не требуется, чтобы какой-то конкретный дополнительный сервис был доступен, вы просто используете IBusinessRules в качестве фактического параметра типа.

3 голосов
/ 12 сентября 2010

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

2 голосов
/ 12 сентября 2010

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

Например, вы можете пойти с простым кастом.Вы получите объект алгоритма с завода, приведете его к соответствующему (определенному) объекту алгоритма, а затем вызовете функцию «Вычислить».

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

Важные вопросы, которые вам нужно задать себе: 1. Сколько раз вам потребуется реализовать определенные функции?2. Есть ли способ решить проблему с добавленным полиморфизмом, вытекающим из базового класса?3. Будут ли пользователи производных объектов знать, что они используют определенный объект, или вы хотите, чтобы они не знали фактического типа?

В общем, что я лично делаю в таких случаях, так это начинаю с самого простогорешение (в данном случае конкретное приведение и вызов функции), и я возвращаюсь и реорганизую, когда у меня есть еще некоторые данные о домене.Если вы чувствительны к «вонючему коду», вы попадете в точку, когда увидите, что там слишком много помех, и вы превратите его в лучшее решение.

1 голос
/ 12 сентября 2010

Я бы изменил это так

interface IBusinessRules
{
    string Perform();
    bool CanCalculate { get; }
    string Calculate();
}

и добавить абстрактный базовый класс (необязательно, но рекомендуется для дальнейшей расширяемости)

public abstract class BusinessRules : IBusinessRules {
    protected BusinessRules() { 
    }

    protected virtual bool CanCalculateCore() {
         return false; // Cannot calculate by default
    }

    protected virtual string CalculateCore() { 
         throw new NotImplementedException("Cannot calculate"); 
    }

    protected abstract string PerformCore();

    #region IBusinessRules Members

    public string Perform()
    {
        return PerformCore();
    }

    public bool CanCalculate
    {
        get { return CanCalculateCore(); }
    }

    public string Calculate()
    {
        return CalculateCore();
    }

    #endregion
}

Так что сайт звонков теперь выглядит аккуратно:

objBase = objfactory.GetObject(1);
if (objBase.CanCalculate) {
    Console.WriteLine(objBase.Calculate());
}

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

0 голосов
/ 12 сентября 2010

Это проблема моделирования домена, связанная с тем, что вы подразумеваете под BusinessRule и IBase в проблемной области.

Что такое IBase? Похоже, это следует назвать IBusinessRule. В этом случае, что означает «Рассчитать» в контексте «бизнес-правила». Если он имеет общее значение в вашем домене, тогда IBusinessRule должен реализовать его, как и другие классы, даже если только как пустой метод.

Если он не имеет общего значения в вашем домене, тогда ваш класс должен реализовать другой интерфейс ICalculable (IAlgorithm?) С вычислением, который вы называете:

ICalculable calculable = obj as ICalculable;
if ( calculable != null ) calculable.Calculate();
...