5

Java 8 の新しいデフォルト メソッドを使用してテンプレート メソッドをリファクタリングしたいと考えています。抽象クラスでプロセス定義のフローがあるとします:

public abstract class FlowManager{
    public void startFlow(){
        phase1();
        phase2();
    }
    public abstract void phase1();
    public abstract void phase2();
}

上記のフローマネージャーを拡張するサブクラスはほとんどなく、各サブクラスは独自のメソッドを実装していphase1ますphase2。次のようなインターフェイスにコードをリファクタリングすることに意味があるのだろうか。

public interface FlowManager{
    public default startFlow(){
        this.phase1();
        this.phase2();
    }
    public void phase1();
    public void phase2();
}

どう思いますか?

4

5 に答える 5

13

テンプレートメソッドパターンを実装するためにデフォルトメソッドを持つインターフェースを使用することは、私には疑わしいようです。

デフォルトのメソッドは、通常 (常にではありませんが) 実装者によってオーバーライドされることを意図しています。インターフェイスのデフォルト メソッドがテンプレート メソッドとして使用された場合、オーバーライド メソッドは、superメソッドを呼び出さない、間違ったタイミングで呼び出す、フェーズが呼び出される順序を変更するなどのプログラミング エラーの影響を受けやすくなります。これらはすべてテンプレート メソッド パターンが回避することを意図したプログラミング エラー。

通常、テンプレート メソッドはオーバーライドされることを意図していません。Java クラスでは、メソッドを作成することでこれを通知できますfinal。インターフェイスは final メソッドを持つことができません。根拠については、この質問を参照してください。したがって、テンプレートとして final メソッドを持つ抽象クラスを使用して、テンプレート メソッド パターンを実装することをお勧めします。

于 2015-06-30T01:26:58.973 に答える
3

以前の回答に加えて、さらに多くの可能性があることに注意してください。まず、テンプレート メソッドを独自のクラスに分離します。

public interface Flow {
    void phase1();
    void phase2();
}

public final class FlowManager {
    private final Flow flow;

    public FlowManager(Flow flow) {
        this.flow = flow;
    }

    public void startFlow() {
        flow.phase1();
        flow.phase2();
    }
}

すでにメソッドを使用している場合は、インターフェースもFlowManager.phaseX実装することができます。Flow

public final class FlowManager implements Flow {
    private final Flow flow;

    public FlowManager(Flow flow) {
        this.flow = flow;
    }

    public void startFlow() {
        flow.phase1();
        flow.phase2();
    }

    @Override
    public void phase1() {
        flow.phase1();
    }

    @Override
    public void phase2() {
        flow.phase2();
    }
}

Flowこのようにして、ユーザーがインターフェイスを実装する必要があることを明示的に通知しますがstartFlow、最終クラスで宣言されているため、テンプレート メソッドを変更することはできません。

Java 8 では、問題を解決するための新しい機能パターンが追加されています。

public final class FlowManager {
    private final Runnable phase1;
    private final Runnable phase2;

    public FlowManager(Runnable phase1, Runnable phase2) {
        this.phase1 = phase1;
        this.phase2 = phase2;
    }

    public void startFlow() {
        phase1.run();
        phase2.run();
    }

    public void phase1() {
        phase1.run();
    }

    public void phase2() {
        phase2.run();
    }
}

このコードは Java 8 より前でも機能しますが、今FlowManagerではラムダまたはメソッド参照を使用して作成できるため、はるかに便利です。

アプローチを組み合わせることもできます。インターフェイスを定義し、ラムダから構築する方法を提供します。

public interface Flow {
    void phase1();
    void phase2();

    static Flow of(Runnable phase1, Runnable phase2) {
        return new Flow() {
            @Override
            public void phase1() {
                phase1.run();
            }

            @Override
            public void phase2() {
                phase2.run();
            }
        };
    }
}

CollectorJava 8のインターフェースも同様の方法で実装されています。ユーザーの好みに応じて、インターフェイスを直接実装するかFlow.of(...)、そこにラムダまたはメソッド参照を使用して渡すことができます。

于 2015-06-30T04:21:31.337 に答える
2

//デザインテンプレートクラス

public class Template {


    protected interface MastSuppler{

        List<Mast> apply(int projectId);
    }

    protected interface Transform<T>{
        List<T> apply(List<Mast> masts);
    }

    protected interface PropertiesConsumer<T>{
        void apply(List<T> properties);
    }

    public <T> void template(int projectId, MastSuppler suppler, Transform<T> transform, PropertiesConsumer<T> consumer){
        System.out.println("projectId is " + projectId);
        //1.List<Mast> masts = step1(int projectId);
        List<Mast> masts = suppler.apply(projectId);
        //2.List<T> properties = step2(List<Mast> masts)
        List<T> properties = transform.apply(masts);

        //3.use or consume these properties(print to console ,save to datebase)

        consumer.apply(properties);
    }   

}

//クライアントで使用

public class Mast {

    public static void main(String[] args) {
        //1.save to db



        new Template().template(1,
                          projectId->getMastsfromMongo(projectId),
                          masts-> masts.stream().map(mast->mast.getName()).collect(Collectors.toList()), 
                          names->System.out.println("save names to db "+ names));
        //new Template(1, id->Arrays, );

        //2.print to console


        new Template().template(2,
                          projectId->getMastsSomewhere(projectId),
                          masts-> masts.stream().map(mast->mast.getLat()).collect(Collectors.toList()), 
                          names->System.out.println("print lons to console "+ names));
    }



    private static List<Mast> getMastsfromMongo(int projectId){

        Mast m1 = new Mast("1", 110, 23);
        Mast m2 = new Mast("2", 111, 13);

        return Arrays.asList(m1, m2);
    }

    private static List<Mast> getMastsSomewhere(int projectId){

        Mast m1 = new Mast("3", 120, 53);
        Mast m2 = new Mast("4", 121, 54);

        return Arrays.asList(m1, m2);
    }





        private String name;
        private double lon;
        private double lat;

        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public double getLon() {
            return lon;
        }
        public void setLon(double lon) {
            this.lon = lon;
        }
        public double getLat() {
            return lat;
        }
        public void setLat(double lat) {
            this.lat = lat;
        }

        public Mast(String name, double lon, double lat) {
            super();
            this.name = name;
            this.lon = lon;
            this.lat = lat;
        }


}
于 2015-11-27T09:42:45.923 に答える
0

どちらのアプローチも機能します。

どちらを使用するかは、他の機能FlowManagerと、後でどのように使用するかによって大きく異なります。

抽象クラスを使用すると、何らかの状態もモデル化する必要がある場合に、非静的フィールドを定義できます。また、プライベートまたは保護されたメソッドを持つこともできます。

一方、インターフェースを使用すると、単一の継承に制約されないため、関連のないクラスによる実装が容易になります。

Javaのチュートリアルでは、「インターフェースと比較した抽象クラス」セクションでそれをかなりうまくまとめています。

http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

于 2015-06-29T21:05:00.037 に答える