简体   繁体   English

这是一种设计模式吗? 如果是这样,它是什么?

[英]Is this a design pattern? If so, what is it?

I'm taking over some code for a project and I've seen a bunch of copied code in multiple classes.我正在接管一个项目的一些代码,并且我在多个类中看到了一堆复制的代码。 The owner is very weary about refactoring this code but I came up with an idea that sounded good to him.所有者对重构此代码感到非常厌倦,但我想出了一个对他来说听起来不错的想法。

Given:鉴于:

Multiple "Client" classes that do NOT have the exact same interface but fairly close:多个“客户端”类不具有完全相同的接口但相当接近:

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

Lets say that FunctionA is function that is EXACTLY the same in each class.假设FunctionA是 function,在每个 class 中完全相同。 The owner somehow thinks that this function needs to be in each class because "it could be different for another client in the future" (fyi: FunctionA converts standard time to military time... so I highly doubt this).所有者以某种方式认为这个 function 需要在每个 class 中,因为“将来对于另一个客户来说可能会有所不同”(仅供参考:FunctionA 将标准时间转换为军用时间......所以我非常怀疑这一点)。

Each of his clients has a special code (ie "abc" or "xyz" in the web.config file so when the client's code is accessed, it gets the correct behavior using code similar to this:他的每个客户端都有一个特殊的代码(即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");
  }
}

Let it be known that I do not in any way think this is ideal.让大家知道,我无论如何都不认为这是理想的。 I've actually fought with my client (who owns this code) in some fairly heated discussions about the decisions he has made with this project so don't shoot the messenger.实际上,我在一些相当激烈的讨论中与我的客户(拥有此代码)就他对这个项目所做的决定进行了激烈的讨论,所以不要射击信使。

My client believes this way of doing things is useful when, say, I want to implement a new Client, I can just run the application and go through some steps until I find and "fix" these thrown exceptions.我的客户认为这种做事方式很有用,比如说,我想实现一个新的客户端,我可以通过一些步骤运行应用程序和 go,直到找到并“修复”这些抛出的异常。 This is why the owner likes how the code is set up this way.这就是为什么所有者喜欢以这种方式设置代码的原因。 However, about half the functions in each Client class are the same for each client OR they are the same for about 80% of the clients.但是,每个客户端 class 中大约一半的功能对于每个客户端是相同的,或者对于大约 80% 的客户端它们是相同的。

I asked him why he just didn't have an abstract class and the reason being was that not every derived class needs to or should implement any of the base classes functions.我问他为什么他没有抽象 class 原因是不是每个派生的 class 都需要或应该实现任何基类函数。 Also, all of the public functions are static (and there are no member variables) so it doesn't make sense to instantiate objects.此外,所有公共函数都是 static(并且没有成员变量),因此实例化对象没有意义。

My Pattern:我的模式:

Using the client classes from above, I would like to implement something like this:使用上面的客户端类,我想实现这样的东西:

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

Here's a flow chart as well to get an understand of how this works:这里还有一个流程图,以了解其工作原理:某种类型的代理模式?

By the look of your code, yes, there is a name for your pattern.从您的代码来看,是的,您的模式有一个名称。

It is called: Procedural Programming它被称为:过程编程

Both ways are a mess to maintain.这两种方式都是一团糟。 Also, the static method thing is causing lots of problems since you can't override them.此外,static 方法会导致很多问题,因为您无法覆盖它们。

You should instead use the factory pattern.您应该改用工厂模式。 First set up your interfaces and clients首先设置你的接口和客户端

// 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
}

Now all you need to do is create the correct client class at run time.现在您需要做的就是在运行时创建正确的客户端 class。 You are about to experience object oriented bliss.您即将体验 object 导向的幸福。

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

Assuming that you don't have typos and fix all "static" issues what you are trying to do is "Template Design pattern".假设您没有拼写错误并修复所有“静态”问题,您正在尝试做的是“模板设计模式”。 More information avaialable here http://www.dofactory.com/Patterns/PatternTemplate.aspx更多信息可在此处获取 http://www.dofactory.com/Patterns/PatternTemplate.aspx

There's a great book called Head First Design Patterns.有一本很棒的书叫做 Head First Design Patterns。 The first chapter discusses creating interfaces for "behaviors."第一章讨论为“行为”创建接口。 You could have a single client class that can use certain behaviors via a client factory.您可以有一个客户端 class 可以通过客户端工厂使用某些行为。

Also look into some IOC (inversion of control) frameworks so that you can easily inject the behaviors into your client classes at runtime.还要研究一些 IOC(控制反转)框架,以便您可以在运行时轻松地将行为注入到客户端类中。

Admitting I just scanned through and did not read everything fully.承认我只是浏览了一下并没有完全阅读所有内容。 ;-) ;-)

There are a couple of ways to tackle this, depending on the exact needs.有几种方法可以解决这个问题,具体取决于具体需求。 If you can fit the same exact interface, you have the option of a factory pattern.如果您可以安装相同的确切界面,则可以选择工厂模式。 Assume that is out, at least directly.假设它已经结束了,至少是直接的。 But, you can set up a facade that makes a common "interface" and then use a factory (or provider, etc).但是,您可以设置一个创建通用“接口”的外观,然后使用工厂(或提供者等)。

If roughly the same work is being done, but with slightly different methods, you can aim for a unit of work or a strategy pattern fairly easily.如果正在完成大致相同的工作,但使用的方法略有不同,那么您可以相当容易地瞄准一个工作单元或策略模式。

In general, you want to get away from switch statements, if at all possible, as you are very likely to break the open/closed principle (open for extension, but closed for change).一般来说,如果可能的话,您希望远离 switch 语句,因为您很可能会破坏打开/关闭原则(为扩展而开放,但为更改而关闭)。

Read up on the Decorator Pattern.阅读装饰器模式。 Here's a sample chapter on it from the most excellent Head First Design Patterns .这是来自最优秀的Head First Design Patterns的示例章节。

http://oreilly.com/catalog/hfdesignpat/chapter/ch03.pdf http://oreilly.com/catalog/hfdesignpat/chapter/ch03.pdf

The decorator pattern is a way of dynamically extending a class with new responsibilities at runtime.装饰器模式是一种在运行时动态扩展具有新职责的 class 的方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM