2

考虑这个例子

界面

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

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

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

4

6 回答 6

3

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

于 2010-09-12T08:04:12.277 回答
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其用作实际的类型参数。

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

于 2010-09-12T09:15:40.227 回答
2

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

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

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

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

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

于 2010-09-12T08:10:16.747 回答
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());
}

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

于 2010-09-12T11:47:21.023 回答
0

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

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

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

ICalculable calculable = obj as ICalculable;
if ( calculable != null ) calculable.Calculate();
于 2010-09-12T08:39:44.673 回答