【问题标题】:Question about Factory Design Architecture关于工厂设计架构的问题
【发布时间】:2010-09-12 07:58:59
【问题描述】:

考虑这个例子

界面

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";
    }
}

我应该如何在 UI 上这样称呼它

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

还有其他解决办法吗?提前致谢

编辑:我重写它以类似于我当前的项目设计

【问题讨论】:

  • 我没有把工厂和开关盒结合起来......如果你必须“决定”,工厂到底有什么用?
  • 是的,系统将根据clientIdentityCode(每个客户端都有唯一的身份代码)决定执行什么方法,所以我必须使用switch case来评估代码
  • 如果使用switch case就不需要Factory,Factory Pattern的整个设计都是基于“不知道”的概念。如果 ClientBusinessRules 类的数量增加,您也必须更新开关/案例。恕我直言,您的设计需要重新考虑。不要仅仅为了它而使用模式。
  • 在您的情况下,如果您将开关盒移动到“静态 void Main”,您的所有问题都将得到解决,忘记所有接口并明确地在那里做您想要的。或者多研究一下工厂模式;)
  • 我的系统有很多客户。我不想为每个客户创建项目的副本。我只需要将每个客户的业务规则分成不同的类,甚至是 dll。,。最好使用哪种设计模式?

标签: c# design-patterns architecture interface factory-pattern


【解决方案1】:

我假设您使用工厂类是为了:

  • 有一个标准外观接受导致业务规则选择配置的参数
  • 封装业务规则配置
  • 将用户与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 作为实际类型参数。

【讨论】:

  • 看起来为Calculate()创建另一个接口是要走的路……谢谢
  • 对“……但不在界面中,因为我只在特殊情况下使用它?” — 一般而言,考虑是否有可能不将特殊情况视为特殊情况,而是与其余代码的体系结构保持一致,这是一个好主意。特殊情况往往会很快成为标准情况,因此值得先考虑一下架构,因为它更便宜。
【解决方案2】:

工厂模式的全部意义在于返回合同的正确实现,这样消费者就不必担心如何实例化它,而只需调用它的方法。您总是可以测试实际类型,转换为它并调用该方法,但这是一个非常糟糕的设计,我不推荐它。消费者不应该对实际类型一无所知。您将需要重新考虑您的设计。

【讨论】:

    【解决方案3】:

    如果你想坚持当前的架构,你可以引入一个新的接口声明

    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();     
    

    维护含义:每次引入新界面时,都必须触摸所有调用代码。由于您发布此代码放置在 UI 代码中,这可能会变得一团糟。

    您引入的每个接口都意味着向类添加行为。如果你有大范围的不同行为,那么我觉得上面的解决方案不太对,因为总是需要使用as 操作和条件执行一个方法。如果你想坚持一些经典的设计模式,这种行为的可变性可以用装饰器模式或策略模式来对抗。它们可以与工厂模式顺利结合。

    【讨论】:

      【解决方案4】:

      在这种情况下可以采用多种方法,这取决于您愿意为获得价值而付出的成本。

      例如,您可以使用简单的强制转换。您将从工厂获取算法对象,将其转换为适当的(特定)算法对象,然后调用“计算”​​函数。

      另一种选择——更通用的选择,也需要更多代码——将在基类中提供查询机制,该机制将提供有关对象内可用功能的信息。这有点类似于在 COM 中查询接口。

      您需要问自己的重要问题是: 1. 您需要实现多少次特定功能? 2. 有没有办法解决来自基类的添加多态性问题? 3. 派生对象的用户会知道他们正在使用特定的对象,还是希望他们不知道实际的类型?

      一般来说,我个人在这种情况下所做的就是从最简单的解决方案开始(在这种情况下,具体的转换和调用函数),当我有更多关于域的数据时,我会回去重构。如果你对“臭代码”很敏感,你会发现有太多的混乱,你会把它重构为更好的解决方案。

      【讨论】:

      • 我在这个问题上停留了一个月,我尝试了不同的方法,但都导致了死胡同,我现在重构我的三层项目,因为它不是那么可扩展,我有一个系统/网站很多客户端(在我的示例中由算法表示)和客户端实现不同的业务规则集。这里的问题是所有客户端都应该使用 1 个网站,所以发生的情况是我的代码太混乱了,很多“如果客户端 1 则执行此操作,否则如果客户端 2 则执行此操作“您知道我的意思
      • 那么在这种情况下,最好的办法是重新评估您的整个设计并开始在更高级别进行重构。如果您在这方面没有足够的经验,我建议您咨询可以教您一些有关重构的知识并帮助您完成此过程(这是一个持续的逐步过程)的人。
      【解决方案5】:

      我会这样修改

      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());
      }
      

      扩展接口的一个大问题是,它根本不会给调用者任何暗示你也可能支持该接口。

      【讨论】:

        【解决方案6】:

        这是一个域建模问题,与问题域中的 BusinessRule 和 IBase 的含义有关。

        IBase 是什么?听起来应该叫IBusinessRule。在这种情况下,计算在“业务规则”的上下文中意味着什么。如果它在您的域中具有通用含义,那么 IBusinessRule 应该实现它,其他类也应该实现它,即使只是作为一个空方法。

        如果它在您的域中没有通用含义,那么您的类应该实现另一个具有计算的接口ICalculable (IAlgorithm?),您将其称为:

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

        【讨论】:

        • 抱歉错别字了它的 IBusinessRule
        猜你喜欢
        • 1970-01-01
        • 2010-12-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多