我正在接管一个项目的代码,我在多个类中看到了一堆复制的代码。业主对重构这段代码感到非常厌倦,但我想出了一个听起来不错的想法。
假设:
多个“客户端”类没有完全相同的接口但相当接近:
class Client1
{
public static string FunctionA(a);
public static string FunctionB(a, b, c);
public static string FunctionC(a, b);
}
class Client2
{
public static string FunctionA(a);
public static string FunctionB(a, b, c);
public static string FunctionC(a, b);
}
class Client3
{
public static string FunctionA(a);
public static string FunctionC(a, b);
public static string FunctionD();
}
... etc
让我们说FunctionA
是每个类中完全相同的函数。所有者不知何故认为这个函数需要在每个类中,因为“未来另一个客户可能会有所不同”(fyi:FunctionA将标准时间转换为军事时间......所以我对此非常怀疑)。
他的每个客户都有一个特殊的代码(即web.config
文件中的“abc”或“xyz”,因此当访问客户端的代码时,使用类似的代码获取正确的行为:
public static string FunctionA(string a)
{
switch(getClientCode())
{
case "abc":
return Client1.FunctionA(a);
case "xyz":
return Client2.FunctionA(a);
case "def":
return Client3.FunctionA(a);
default:
throw new Exception("code not supported");
}
}
让我们知道,我绝对不认为这是理想的。我实际上与我的客户(拥有此代码)在一些关于他所做出的决定的相当激烈的讨论中进行过斗争。用这个项目做的所以不要拍摄信使。
我的客户认为,当我想要实现一个新客户端时,这种做事方式很有用,我可以运行应用程序并执行一些步骤,直到找到并“修复”这些抛出的异常。这就是所有者喜欢这种方式设置代码的原因。但是,每个客户端类中大约一半的功能对于每个客户端是相同的,或者对于大约80%的客户端它们是相同的。
我问他为什么他没有抽象类,原因是不是每个派生类都需要或者实现任何基类函数。此外,所有公共函数都是静态的(并且没有成员变量),因此实例化对象没有意义。
我的模式:
使用上面的客户端类,我想实现类似的东西:
class Client1
{
// FunctionA is the same for each class
//public static string FunctionA(a);
public static string FunctionB(string a, string b, string c);
// Client1 and Client3 share the same code.
//public static string FunctionC(a, b);
}
class Client2
{
// FunctionA is the same for each class
//public static string FunctionA(a);
public static string FunctionB(string a, string b, string c);
public static string FunctionC(string a, string b);
}
class Client3
{
// FunctionA is the same for each class
//public static string FunctionA(a);
// Client1 and Client3 share the same code.
//public static string FunctionC(a, b);
public static string FunctionD();
}
... etc.
class DefaultClient
{
public static string FunctionA(string a);
public static string FunctionB(string a, string b, string c);
public static string FunctionC(string a, string b);
}
class ProxyUtility
{
private static string getClientCode();
public static string FunctionA(string a)
{
switch (getClientCode())
{
case "abc":
case "def":
case "xyz":
return DefaultClient.FunctionA(a);
default:
throw new Exception("code not supported");
}
}
public static string FunctionB(string a, string b, string c)
{
switch (getClientCode())
{
case "abc":
case "xyz":
return DefaultClient.FunctionB(a, b, c);
case "def":
return string.Empty; // or throw an exception since they don't support this
default:
throw new Exception("code not supported");
}
}
public static string FunctionC(string a, string b)
{
switch (getClientCode())
{
case "abc":
case "def":
return DefaultClient.FunctionC(a, b);
case "xyz":
return Client2.FunctionC(a, b);
default:
throw new Exception("code not supported");
}
}
public static string FunctionD()
{
switch (getClientCode())
{
case "abc":
case "xyz":
return string.Empty; // or throw an exception since they don't support this function.
case "def":
return Client3.FunctionD();
default:
throw new Exception("code not supported");
}
}
}
这是一个流程图,以了解其工作原理:
答案 0 :(得分:5)
通过代码的外观,是的,您的模式有一个名称。
它被称为:程序编程
答案 1 :(得分:3)
这两种方式都很难维护。此外,静态方法会导致很多问题,因为您无法覆盖它们。
您应该使用工厂模式。首先设置接口和客户端
// put all methods any client will ever need here
public interface IClient
{
string FunctionA(string a);
string FunctionB(string a, string b, string c);
string FunctionC(string a, string b);
string FunctionD();
}
// now give a nice default implementation
internal abstract class DefaultClient : IClient
{
//all clients have the same functionA, for now, don't even allow overriding
public string FunctionA(string a)
{
return "hello: " + a;
}
//function B and C differ from client to client make them abstract
public abstract string FunctionB(string a, string b, string c);
public abstract string FunctionC(string a, string b);
//functionD isn't usually needed, but sometimes has an implementation
public virtual string FunctionD()
{
// do nothing I guess
}
}
internal class Client1 : DefaultClient
{
// implement functionB and functionC here
}
internal class Client2 : DefaultClient
{
// implement functionB and functionC here
}
internal class Client3 : DefaultClient
{
// implement functionB, functionC and functionD here
}
现在您需要做的就是在运行时创建正确的客户端类。您即将体验面向对象的幸福。
// here is the factory
public static class ClientFactory
{
public static IClient GetClient(string clientCode)
{
// only 1 place in the code with a switch statement :)
switch (clientCode)
{
case "abc": return new Client1();
case "def": return new Client2();
case "xyz": return new Client3();
default: throw new Exception("code not supported: " clientCode);
}
}
}
答案 2 :(得分:2)
假设您没有拼写错误并修复所有“静态”问题,那么您要尝试的是“模板设计模式”。 更多信息可在此处http://www.dofactory.com/Patterns/PatternTemplate.aspx
答案 3 :(得分:0)
有一本名为Head First Design Patterns的好书。第一章讨论为“行为”创建接口。您可以拥有一个可以通过客户端工厂使用某些行为的客户端类。
还要研究一些IOC(控制反转)框架,以便您可以在运行时轻松地将行为注入到客户端类中。
答案 4 :(得分:0)
承认我刚刚扫过并没有完全阅读所有内容。 ; - )
根据具体需要,有几种方法可以解决这个问题。如果您可以使用相同的界面,则可以选择工厂模式。假设至少是直接的。但是,您可以设置一个外观,使其成为一个通用的“界面”,然后使用工厂(或提供商等)。
如果大致完成相同的工作,但方法略有不同,您可以相当轻松地瞄准工作单元或策略模式。
一般情况下,如果可能的话,您希望摆脱switch语句,因为您很可能违反开放/封闭原则(对于扩展打开,但对于更改而关闭)。
答案 5 :(得分:0)
阅读装饰模式。以下是最优秀Head First Design Patterns的一个示例章节。
http://oreilly.com/catalog/hfdesignpat/chapter/ch03.pdf
装饰器模式是一种在运行时动态扩展具有新职责的类的方法。