考虑这个例子
界面
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());
还有其他解决方案吗?提前谢谢
编辑:我将其重写为类似于我当前的项目设计
答案 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的接口ICalculable
(IAlgorithm
?),您将其称为:
ICalculable calculable = obj as ICalculable;
if ( calculable != null ) calculable.Calculate();