关于工厂设计架构的问题

时间:2010-09-12 07:58:59

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

考虑这个例子

界面

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

还有其他解决方案吗?提前谢谢

  

编辑:我将其重写为类似于我当前的项目设计

6 个答案:

答案 0 :(得分:3)

工厂模式的重点是返回合同的正确实现,以便消费者不必担心如何实例化它,而只是调用它的方法。你总是可以测试实际的类型,强制转换它并调用方法,但这是一个非常糟糕的设计,我不推荐它。消费者不应该对实际类型有任何了解。您需要重新考虑您的设计。

答案 1 :(得分:3)

我认为你正在使用工厂类来:

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

答案 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操作和条件执行方法。如果你想坚持一些经典的设计模式,这种行为的可变性可以用装饰模式或策略模式来对抗。它们可以与工厂模式顺利结合。

答案 3 :(得分:2)

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

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

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

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

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

答案 4 :(得分:1)

我会像这样修改它

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

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

答案 5 :(得分:0)

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

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

如果您的域中没有通用含义,那么您的类应该实现另一个具有Calculate的接口ICalculableIAlgorithm?),您将其称为:

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