这个设计模式有名字吗? (具有仅调用构造函数的实现的基类)

Posted

技术标签:

【中文标题】这个设计模式有名字吗? (具有仅调用构造函数的实现的基类)【英文标题】:Is there a name for this Design Pattern? (Base class with implementations that only invoke constructor) 【发布时间】:2016-12-29 01:54:07 【问题描述】:

编辑:我意识到这种模式很像currying,这是一种函数式程序员用来在调用之前指定函数参数的技术。这里的区别是我们在对象上柯里化构造函数,而不是简单地柯里化函数。


在几个项目中,我发现自己使用了这种奇怪的设计模式,我找不到它的名字。它有名字吗?你告诉我,也许这只是一种不好的做法。

设计模式

使用此模式,您将...

    没有抽象方法的抽象基类(我们可以稍后讨论)。 基类的许多“实现”。但是,这些实现将仅用于调用基类的构造函数

Java 示例(带有假设场景)

我将定义一个假设场景来提供一些背景信息。

场景:

Bob 正在编写一个用于扫描源代码的小型 API。他希望能够检查注释是否在源代码中的给定索引处开始/结束。

这是 Bob 的代码。

1。抽象基类

public abstract class CommentDetector 

    private final String startPattern;
    private final String endPattern;

    protected CommentDetector(String startPattern, String endPattern) 
        this.startPattern = startPattern;
        this.endPattern = endPattern;
    

    public boolean commentStartsAt(int index, String sourceCode) 
        // ...
    

    public boolean commentEndsAt(int index, String sourceCode) 
        // ...
    


您可能想知道为什么它是抽象的但没有抽象方法。这仅仅是因为 Bob 不希望您直接实例化它。 Bob 希望你编写一个 CommentDetector 的实现,然后实例化 that。这是 Bob 的两个实现...

2。一些实现

一个用于 Java 中的多行 cmets:

public class JavaMultiLineCommentDetector extends CommentDetector 

    public JavaMultiLineCommentDetector() 
        super("/*", "*/");
    

一个用于 Java 中的单行 cmets:

public class JavaSingleLineCommentDetector extends CommentDetector 

    public JavaSingleLineCommentDetector() 
        super("//", "\n");
    

Bob 为我们编写了这些实现,因此我们可以编写 new JavaMultiLineCommentDetector() 而不是 new CommentDetector("/*", "*/")

如果需要,Bob 还鼓励您为其他语言编写自己的实现。


总结

感觉这种设计模式的目的是通过预定义构造函数调用来提高代码的可读性。

这种设计模式给代码带来了多态的感觉(尽管它可能真正是多态的)。

编写新的实现既快速又简单。

实现不相互依赖,可以独立编译/部署。

这个设计模式有名字吗?

【问题讨论】:

它不是以任何有意义的方式多态的:/ 似乎这种模式可以通过静态辅助方法同样好地实现。 或将其设为枚举 嗯...它也可能是一种反模式,可能会导致像new CommentDetector("//", "\n") ;这样的代码(这会使客户端库膨胀) @BrandonIbbotson - 它是 - 它正在创建一个匿名类。 看起来意图是创建一个extensible enum。 CommentDetector 将是枚举类。 JavaMultiLineCommentDetectorJavaSingleLineCommentDetector 将是枚举常量,并且客户端也可以扩展 CommentDetector 【参考方案1】:

感觉这种设计模式的目的是通过预定义构造函数调用来提高代码的可读性。

在 OOP 中,不应使用构造函数来定义合约,因为:

它不如抽象方法精确 具体类可能会误用超级构造函数 它不是为以 OOP 方式扩展而设计的,因为它不允许覆盖行为。

通常,当您希望允许您的类客户选择在处理中必须使用的对象时,更常用的是工厂方法或构建器设计模式:

抽象类:

public abstract class CommentDetector 

  private final String startPattern;
  private final String endPattern;

  public abstract String getStartPattern();
  public abstract String getEndPattern();

  public boolean commentStartsAt(int index, String sourceCode)        
      getStartPattern()...
  

  public boolean commentEndsAt(int index, String sourceCode)
      getEndPattern()....

具体类

public class JavaSingleLineCommentDetector extends CommentDetector 

     public String getStartPattern()
         return "//";
     

     public abstract String getEndPattern()
         return "\n";
     

这种设计模式给代码带来了多态的感觉(即使 它可能/可能不是真正的多态)。

编写新的实现是 快速简单。

在这种情况下确实如此,因为类变得可扩展。如果需要,您可以使用具体类并覆盖任何工厂方法。

【讨论】:

这很好。但是有没有办法限制getStartPattern()getEndPattern() 的可见性? 谢谢 :) 不,因为它允许客户端类定义自己的实现(或重用现有实现来覆盖 id)。如果要允许可扩展性,则必须允许客户公共合同 IMO 将其称为工厂方法模式的示例有点牵强... @Oliver Charlesworth 对不起,我明白了。你是对的,因为创建的对象是一个字符串。它更像是一个建设者。没有?【参考方案2】:

Factory Method 模式有什么问题?

public class CommentDetector 
    //...

    private CommentDetector(String startPattern, String endPattern) 
        this.startPattern = startPattern;
        this.endPattern = endPattern;
    

    public static CommentDetector giveMeThisInstance() 
        return new CommentDetector("//", "\n");
    
    public static CommentDetector giveMeThatInstance() 
        return new CommentDetector("/*", "*/");
        

这种方法将节省您的permgen 内存。并且对于我的主观看法更加一致:将所有内容保持为单一类,但创建具有不同属性的对象。

【讨论】:

这似乎是更好的方法之一。但是,如果我想添加更多语言,它会污染原始的 CommentDetector 类。 @BrandonIbbotson 您可以使构造函数受保护,然后将工厂放在另一个类甚至多个其他类中。 @Polygnome 好点。这种方法为我们提供了我最初想法的所有好处。 @Rudziankoŭ 这不是FactoryMethod pattern,但仍然是一个不错的选择。您还可以通过变量名传达评论检测器的味道:CommentDetector javaSingleLineDetector = new CommentDetector("//", "\n"); @BrandonIbbotson,创建类只是为了调用super(); 也是一种重复,不是吗?【参考方案3】:

使用 可扩展枚举 模式(已在 cmets 中注明)您还可以避免继承:

public interface CommentDelimiter 
    String getStartPattern();
    String getEndPattern();


public interface CommentDetector 
    boolean commentStartsAt(int index, String sourceCode);
    boolean commentEndsAt(int index, String sourceCode);


public enum CommentDetectors implements CommentDetector 
    JAVA_MULTILINE(CommentDelimiters.JAVA_MULTILINE),
    JAVA_SINGLELINE(CommentDelimiters.JAVA_SINGLELINE);

    // ... store commentDelimiter

    public boolean commentStartsAt(int index, String sourceCode) 
        // ... using commentDelimiter.getStartPattern()
    

    public boolean commentEndsAt(int index, String sourceCode) 
        // ... using commentDelimiter.getEndPattern()
    


public enum CommentDelimiters implements CommentDelimiter 
    JAVA_MULTILINE("/*", "*/"),
    JAVA_SINGLELINE("//", "\n");

    // ... store start, end

【讨论】:

我喜欢你在这里干净地划分定界符和检测器的方式。这是我没有想到的。我会进一步将CommentDelimitersCommentDetectors 拆分为每种语言的多个文件。

以上是关于这个设计模式有名字吗? (具有仅调用构造函数的实现的基类)的主要内容,如果未能解决你的问题,请参考以下文章

实验三

使构造函数仅接受 C# 中具有 [Serializable] 属性的对象

C# 关于 构造函数问题 关于对象实例化

vc 父类实现一个虚函数,子类继承并也实现这个虚函数,子类调用这个虚函数,父类这个虚函数会不会执行?

基类构造函数真的在派生类构造函数之前调用吗

两个有意思的模式在ECMAScript中的实现