保护模板模式中的钩子

时间:2016-02-20 21:28:45

标签: java oop design-patterns access-modifiers

我在Java中实现模板模式。让我们假设以下代码:

public abstract class A {

  public final void work() {
    doPrepare();
    doWork();
  }

  protected abstract void doPrepare();

  protected abstract void doWork();
}

public final class B extends A {

  @Override 
  protected abstract void doPrepare() { /* do stuff here */ }

  @Override 
  protected abstract void doWork() { /* do stuff here */ }
}

public final class Main {

  public static void main(String[] args) {
    B b = new B();
    b.work();
  }
}

我认为这是一个问题,人们很容易无意中拨打b.doWork()而不是总是打电话给b.work()。如果可能的话,最优雅的解决方案是什么,“隐藏”钩子?

2 个答案:

答案 0 :(得分:3)

您确实在使用模板模式。你可以做两件事,然后躲避"来自外部用户的详细信息:

1)了解您的access modifiers

                   Access Levels  
Modifier    | Class | Package | Subclass | World | 
--------------------------------------------------
public      | Y     |  Y      | Y        | Y     |  
protected   | Y     |  Y      | Y        | N     |  
no modifier | Y     |  Y      | N        | N     |  
private     | Y     |  N      | N        | N     |  

由于缺乏坚定的黑客在过去通过这些保护措施欺负他们的方式,这些可以让偶然的编码人员无意中调用最好的方法。程序员会欣赏这一点。没有人想使用混乱的API。

目前b.doWork()protected。因此,如果您的main()在课堂上main()(它不是),子类A(它不是B,那么它只会显示在您的b.doWork()中),或在同一个包中(不清楚,你没有发布任何有关你的代码所在的软件包的线索)。

这引出了一个问题,你是谁试图阻止看到A?如果您正在创建的包只是您正在开发的包装,那可能并不是那么糟糕。如果很多人在这个包中乱搞许多其他课程,那么他们就不太可能熟悉课程Bprotected。在这种情况下,您不希望所有人都可以看到所有内容。这里只允许类和子类访问会很好。但是没有允许子类访问的修饰符,也没有允许包访问。只要您将main()子类化为最佳,您就可以做到。考虑到这一点,不要创建包含大量课程的包。保持包裹紧密和集中。这样,大多数人都会在包装外面工作,看起来很简单。

简而言之,如果您希望b.doWork()阻止main()package some.other.package public final class Main { ... } 置于另一个套餐中,

b.doWork()

2)使用当前代码很难理解下一条建议的价值,因为它解决了只有在扩展代码时才会出现的问题。但它确实与保护人们看不到像public static void main(String[] args) { A someALikeThingy = new B(); // <-- note use of type A someALikeThingy.work(); //The name is silly but shows we've forgotten about B //and know it isn't really just an A :) }

这样的东西密切相关

What does "program to interfaces, not implementations" mean?

在你的代码中,这意味着如果main看起来像这样会更好:

A

为什么呢?使用B类型与类型A有什么关系?好interface更接近接口。请注意,此处我并不仅仅指您在使用关键字B时所获得的内容。我的意思是类型AB类型的具体实现,如果我不必编写针对A的代码,我真的不愿意,因为谁知道什么是奇怪的非{ {1}}事B有。这里很难理解,因为main中的代码很简短。甜。此外,AB的公开接口并非完全不同。它们都只有一个公共方法work()。想象一下,main()漫长而复杂,想象B有各种非A方法悬挂它。现在假设你需要创建一个你想要主要处理的类C。看到虽然主要是B,但主要的每一行知道B只是一些简单的A之类的东西,这并不令人感到欣慰。除了new之后的部分,这可能是真的,除了对main()做任何事情,但更新new。实际上,这不是为什么使用强类型语言有时候会很好吗?

&LT;咆哮&GT;
如果您认为在使用new B()之后更新该部分工作太多,那么您并不孤单。特别值得注意的是,dependency injection movement产生了一系列框架,而这些框架实际上更多地是关于自动化对象构造,而不是任何构造函数可以让你做的简单注入。 &LT; /咆哮&GT;

更新

我从你的评论中看到你不愿意创建小型紧包。在这种情况下,请考虑放弃基于继承的模板模式,以支持基于组合的策略模式。你仍然得到多态性。它不会免费发生。更多的代码编写和间接。但是即使在运行时也可以改变状态。

这似乎反直觉,因为现在doWork()必须公开。这有什么样的保护?那么现在你可以完全隐藏B,甚至隐藏在AHandler内。这是一些人性化的Facade类,它包含曾经是您的模板代码但仅暴露work()A可以只是interface,因此AHandler仍然不知道它有B。这种方法在依赖注入人群中很受欢迎,但肯定没有普遍的吸引力。有些人每次都盲目地惹恼了许多人。你可以在这里读更多关于它的内容: Prefer composition over inheritance?

答案 1 :(得分:2)

您可以做的最简单,最明显的事情是使用其他软件包中的A -> B层次结构。

如果由于某种原因你不能这样做,那么你可以使用一个接口并使你的B类包装实际来自A的类。像这样:

public interface Worker {

    void work();
}

public abstract class A implements Worker {

    @Override
    public final void work() {
        doPrepare();
        doWork();
    }

    protected abstract void doPrepare();

    protected abstract void doWork();
}

public static class B implements Worker {  // does not extend A, but implements Worker

    private final A wrapped = new A() { // anonymous inner class, so it extends A

        @Override
        protected void doPrepare() {
            System.out.println("doPrepare");
        }

        @Override
        protected void doWork() {
            System.out.println("doWork");
        }
    };

    @Override
    public void work() { // delegate implementation to descendant from A
        this.wrapped.work();
    }
}

这样,受保护的方法仍隐藏在从A扩展的匿名内部类中,BB的私有最终成员。关键是A不会扩展Worker,而是通过委托给匿名内部类来实现work()接口的B方法。因此,即使在属于同一个包的类中调用work()&#39; B b = new B(); b.work(); // this method is accessible via the Work interface 方法,受保护的方法也不可见。

doPrepare
doWork

输出:

java -jar jarfilename.jar