繁体   English   中英

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

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

我正在接管一个项目的一些代码,并且我在多个类中看到了一堆复制的代码。 所有者对重构此代码感到非常厌倦,但我想出了一个对他来说听起来不错的想法。

鉴于:

多个“客户端”类不具有完全相同的接口但相当接近:

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是 function,在每个 class 中完全相同。 所有者以某种方式认为这个 function 需要在每个 class 中,因为“将来对于另一个客户来说可能会有所不同”(仅供参考: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");
  }
}

让大家知道,我无论如何都不认为这是理想的。 实际上,我在一些相当激烈的讨论中与我的客户(拥有此代码)就他对这个项目所做的决定进行了激烈的讨论,所以不要射击信使。

我的客户认为这种做事方式很有用,比如说,我想实现一个新的客户端,我可以通过一些步骤运行应用程序和 go,直到找到并“修复”这些抛出的异常。 这就是为什么所有者喜欢以这种方式设置代码的原因。 但是,每个客户端 class 中大约一半的功能对于每个客户端是相同的,或者对于大约 80% 的客户端它们是相同的。

我问他为什么他没有抽象 class 原因是不是每个派生的 class 都需要或应该实现任何基类函数。 此外,所有公共函数都是 static(并且没有成员变量),因此实例化对象没有意义。

我的模式:

使用上面的客户端类,我想实现这样的东西:

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

这里还有一个流程图,以了解其工作原理:某种类型的代理模式?

从您的代码来看,是的,您的模式有一个名称。

它被称为:过程编程

这两种方式都是一团糟。 此外,static 方法会导致很多问题,因为您无法覆盖它们。

您应该改用工厂模式。 首先设置你的接口和客户端

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

现在您需要做的就是在运行时创建正确的客户端 class。 您即将体验 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);
      }
  }
}

假设您没有拼写错误并修复所有“静态”问题,您正在尝试做的是“模板设计模式”。 更多信息可在此处获取 http://www.dofactory.com/Patterns/PatternTemplate.aspx

有一本很棒的书叫做 Head First Design Patterns。 第一章讨论为“行为”创建接口。 您可以有一个客户端 class 可以通过客户端工厂使用某些行为。

还要研究一些 IOC(控制反转)框架,以便您可以在运行时轻松地将行为注入到客户端类中。

承认我只是浏览了一下并没有完全阅读所有内容。 ;-)

有几种方法可以解决这个问题,具体取决于具体需求。 如果您可以安装相同的确切界面,则可以选择工厂模式。 假设它已经结束了,至少是直接的。 但是,您可以设置一个创建通用“接口”的外观,然后使用工厂(或提供者等)。

如果正在完成大致相同的工作,但使用的方法略有不同,那么您可以相当容易地瞄准一个工作单元或策略模式。

一般来说,如果可能的话,您希望远离 switch 语句,因为您很可能会破坏打开/关闭原则(为扩展而开放,但为更改而关闭)。

阅读装饰器模式。 这是来自最优秀的Head First Design Patterns的示例章节。

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

装饰器模式是一种在运行时动态扩展具有新职责的 class 的方法。

暂无
暂无

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

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