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

Is there a name for this Design Pattern? (Base class with implementations that only invoke constructor)

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


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

设计模式

使用此模式,您将拥有...

  1. 抽象基础class没有抽象方法(我们稍后再讨论)。
  2. 基class的很多"implementations"。但是,这些实现 仅用于调用基 class.
  3. 的构造函数

A Java 示例(假设场景)

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

场景:

Bob 正在编写一个用于扫描源代码的小 API。他希望能够检查源代码中给定索引处的注释 starts/ends。

这是 Bob 的代码。

1。抽象基础 Class

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 中的多行注释:

public class JavaMultiLineCommentDetector extends CommentDetector {

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

一个用于Java中的单行注释:

public class JavaSingleLineCommentDetector extends CommentDetector {

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

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

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


总结

这个设计模式有名字吗?

It feels like the intent of this design pattern is to improve readability of the code by pre-defining constructor calls.

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

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

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

摘要class:

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()....
}

混凝土class

public class JavaSingleLineCommentDetector extends CommentDetector {

     public String getStartPattern(){
         return "//";
     }

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

This design pattern gives a polymorphic feel to the code (even though it may/may not be truly polymorphic).

Writing new implementations is quick and easy.

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

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 内存。而我的主观观点更一致:保持一切单一 class,但创建具有不同属性的对象。

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

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
}