Java 8と戦略パターン

Design Patterns(Elizabeth Freemanおよびその他)の潜在的に可能な継続。



2017年の庭で。 若い学生のマーティンは、シニア開発者のジョーとインターンシップに参加しました。 彼は、機能インターフェース、ラムダ式、およびその他の革新に重点を置いた最新の教科書を使用して、Javaを1年にわたって綿密に研究しました。



ジョーは、マーティンがデザインパターンを知っているかどうか疑問に思いました。 マーティンはまったく知りませんでした。 そして、ジョーはマーティンをどのようにトレーニングするかを理解しました。 彼は、彼の意見では、設計パターンなしでソリューションを維持するのが難しいタスクを彼に与えます。 Martinがアプリケーションを作成するとき、Joeは最初にアーキテクチャの拡張に苦しみ、その後、最初からすばらしいパターンを作成するように設計すると、拡張がどれほど簡単かを示します。 しかし、彼はすぐにマーティンに拡張の方向について警告することにしました。もし彼自身が設計パターンを知らずにうっかり実装できるとしたらどうでしょうか? 若者は非常に独創的です。



ジョー:マーティン、あなたに責任のある仕事があります。 アヒルの池シミュレータのプロトタイプを作成する必要があります。 アヒルのモデルを作成します。彼らは鳴き、飛び、泳ぎ、他のことをします。 アクションの代わりに、コンソールにテキストメッセージを表示するスタブを使用します。 アヒルは、ゴム製のアヒル、木製、新聞、手アヒル、アヒルの子を持つアヒルなど、さまざまなタイプのものであることを覚えておいてください。 そのため、すべての種類のアヒルが原則的にいんちきしたり、飛んだり、泳いだり、他のアクションを実行できるわけではありません。 一部のアヒルは、最終的に新しい機会を獲得するか、失う可能性があります。 このアプリケーションを長期間サポートする必要があり、当局は新しいタイプのアヒルを思い付くことに注意してください。



マーティンは静かに仕事を始めました。



ふむ アヒルにはさまざまな種類があります。 異なる種のアヒルには異なるスキルセットがあります。 これらのセットは、時間の経過とともに動的に変化したり、数量が増減したりする可能性があり、同じタイプの他のセットに置き換えられます。 一部の変数でアヒルのスキル、つまり行動、つまり機能を維持することは素晴らしいことです。 これにはラムダ式が役立ちます。 たとえば、次のような動作を保存できます。



Runnable fly = () -> System.out.println(" ");
      
      





そして、次のように実行します:



 fly.run();
      
      





振る舞いを変数に保存すると、継承は言うまでもなく、オブジェクトの存続期間中に動的に、他の振る舞いにいつでも置き換えることができます。 また、動作の数も変わる可能性があるため、アクションごとに独自の変数を開始することはできませんが、動的に変化するデータ構造に保存できます。 たとえば、セット内。 または、Map <T、U>で、ビヘイビアのテキスト識別子をキーとして指定すると、このビヘイビアを他のビヘイビアと区別できなくなります。 おそらく、独自のクラスを作成し、そのオブジェクトをアヒルの基本クラスのフィールドに保存して、動作を保存および操作することは価値があります。 それから始めましょう:



 package patterns.and.lambdas.ducks; import java.util.concurrent.ConcurrentSkipListMap; /** *    . * * @param <T> *   : Runnable, Callable<V>, Supplier<T>, BooleanSupplier, * Consumer<T>, BiConsumer<T,U>, Predicate<T>, BiPredicate<T,U>, Function<T,R>, * BiFunction<T,U,R>, UnaryOperator<T>, BinaryOperator<T>,    * {@link java.util.function}       . */ public class BehaviorRegistry<T> { public ConcurrentSkipListMap<String, T> map = new ConcurrentSkipListMap<>(); public void add(final String behaveName, final T behaveFunc) { this.assertContainsNameNot(behaveName); BehaviorRegistry.assertArgNotNull(behaveFunc); this.map.put(behaveName, behaveFunc); } public boolean contains(final String behaveName) { BehaviorRegistry.assertArgNotNull(behaveName); this.assertMapNotNull(); return this.map.containsKey(behaveName) && (this.map.get(behaveName) != null); } public T get(final String behaveName) { this.assertContainsName(behaveName); return this.map.get(behaveName); } public void replace(final String behaveName, final T behaveFunc) { this.assertContainsName(behaveName); BehaviorRegistry.assertArgNotNull(behaveFunc); this.map.put(behaveName, behaveFunc); } public void remove(final String behaveName) { this.assertContainsName(behaveName); this.map.remove(behaveName); } protected static void assertArgNotNull(final Object arg) { if ((arg instanceof String) && !"".equals(arg)) return; if (arg != null) return; throw new RuntimeException(" ."); } protected void assertContainsName(final String behaveName) { BehaviorRegistry.assertArgNotNull(behaveName); this.assertMapNotNull(); if (!this.contains(behaveName)) { throw new RuntimeException(" \"" + behaveName + "\"  ."); } } protected void assertContainsNameNot(final String behaveName) { BehaviorRegistry.assertArgNotNull(behaveName); this.assertMapNotNull(); if (this.contains(behaveName)) { throw new RuntimeException(" \"" + behaveName + "\"  ."); } } protected void assertMapNotNull() { if (this.map == null) throw new RuntimeException(" map."); } }
      
      





このクラスは一般化されているため、BehaviorRegistryクラスに能力起動メソッドを実装しません。したがって、特定のインスタンスの基礎となる機能インターフェイスがわからないため、実行中の関数の名前がわかりません:run()、call()、accept( )、test()、apply()など。これらの関数の引数の数とタイプ、およびこれらの関数が返すものはわかりません。



Duckクラスで使用します。



 package patterns.and.lambdas.ducks; public class Duck { protected BehaviorRegistry<Runnable> behaviors = new BehaviorRegistry<>(); public void perform(final String behaveName) { this.behaviors.get(behaveName).run(); } /** *           . */ public void performAll() { this.behaviors.map.descendingKeySet().forEach(this::perform); System.out.println("----------------------------------------------"); } }</nobr>
      
      





原則として、それだけです。 継承とクラス階層さえも必要ありませんでした。 Duckオブジェクトを作成し、behaviorsフィールドに必要な数の異なる動作を保存し、必要に応じて実行します。 結果のアーキテクチャは次のとおりです。







このアーキテクチャでは、duckの動作はint数値変数の匿名値である6と同じ匿名値であるため、動作にはダイアグラムに単一の長方形はありません。 整数だけが整数である場合、int number変数が格納されている数値を問わないのと同様に、アーキテクチャがRunnable機能インターフェイスを満たしていれば、動作の動作と配置方法は関係ありません。



enumの形式でいくつかの動作のディレクトリを事前に準備しておくと、アーキテクチャを操作しやすくなります。



 package patterns.and.lambdas.ducks; import java.util.function.BiConsumer; /** *    . */ public enum EBehaviors { Display("", () -> System.out.println(" ")), Fly("", () -> System.out.println(" ")), Sleep("", () -> System.out.println("Zzzz")), Quack("", () -> System.out.println("--")), Propagate("", () -> System.out.println("O_o")); public String name; public Runnable func; private EBehaviors(final String m_name, final Runnable m_func) { this.name = m_name; this.func = m_func; } public void sendTo(final BiConsumer<String, Runnable> someApiFunction) { someApiFunction.accept(this.name, this.func); } public void sendTo(final BiConsumer<String, Runnable> someApiFunction, final Runnable m_func) { someApiFunction.accept(this.name, m_func); } }
      
      





これで、特定の操作を安全に開始できます。



 package patterns.and.lambdas.ducks; import java.util.stream.Stream; import patterns.and.lambdas.ducks.Duck; import patterns.and.lambdas.ducks.EBehaviors; public class Test { public static void main(final String[] args) { Runnable behaviorFunc = null; final Duck mallardDuck = new Duck(); behaviorFunc = () -> System.out.println(" "); EBehaviors.Display.sendTo(mallardDuck.behaviors::add, behaviorFunc); EBehaviors.Fly.sendTo(mallardDuck.behaviors::add); EBehaviors.Quack.sendTo(mallardDuck.behaviors::add); final Duck redheadDuck = new Duck(); behaviorFunc = () -> System.out.println("  "); EBehaviors.Display.sendTo(redheadDuck.behaviors::add, behaviorFunc); EBehaviors.Fly.sendTo(redheadDuck.behaviors::add); EBehaviors.Quack.sendTo(redheadDuck.behaviors::add); final Duck rubberDuck = new Duck(); behaviorFunc = () -> System.out.println("  "); EBehaviors.Display.sendTo(rubberDuck.behaviors::add, behaviorFunc); EBehaviors.Quack.sendTo(rubberDuck.behaviors::add); final Duck decoyDuck = new Duck(); behaviorFunc = () -> System.out.println("  "); EBehaviors.Display.sendTo(decoyDuck.behaviors::add, behaviorFunc); final Duck exclusiveDuck = new Duck(); behaviorFunc = () -> System.out.println("  "); EBehaviors.Display.sendTo(exclusiveDuck.behaviors::add, behaviorFunc); behaviorFunc = () -> System.out.println("   <==  "); exclusiveDuck.behaviors.add(" ", behaviorFunc); final Duck[] ducks = { mallardDuck, redheadDuck, rubberDuck, decoyDuck, exclusiveDuck }; //    . System.out.println("############################################## 1"); Stream.of(ducks).forEachOrdered(Duck::performAll); //      . System.out.println("############################################## 2"); behaviorFunc = () -> System.out.println("! <==   RunTime"); EBehaviors.Display.sendTo(redheadDuck.behaviors::replace, behaviorFunc); redheadDuck.performAll(); //      . System.out.println("############################################## 3"); EBehaviors.Propagate.sendTo(mallardDuck.behaviors::add); EBehaviors.Sleep.sendTo(mallardDuck.behaviors::add); mallardDuck.behaviors.map.forEach((name, func) -> { final Runnable newFunc = () -> { func.run(); System.out.println(" ^^^^^ "); }; mallardDuck.behaviors.replace(name, newFunc); }); mallardDuck.performAll(); //       ,     . System.out.println("############################################## 4"); for (final Duck duck : ducks) { Stream.of(EBehaviors.values()).map(val -> val.name).filter(duck.behaviors::contains) .forEach(duck.behaviors::remove); } Stream.of(ducks).forEachOrdered(Duck::performAll); } }
      
      





結果は次のとおりです。



  ##################################################### 1
私はマガモです
私は飛んでいます
いんちき
 ----------------------------------------------
私は赤毛のアヒルです
私は飛んでいます
いんちき
 ----------------------------------------------
私はゴム製のアヒルです
いんちき
 ----------------------------------------------
私は木製のアヒルです
 ----------------------------------------------
私は排他的なアヒルです
炎を吐き出す<==排他的動作
 ----------------------------------------------
 ################################################### 2
 Kryayaaaaaaaaa!  <== RunTimeで変更
私は飛んでいます
いんちき
 ----------------------------------------------
 ################################################### 3
 Zzzz
    ^^^^^後遺症
 O_o
    ^^^^^後遺症
私はマガモです
    ^^^^^後遺症
私は飛んでいます
    ^^^^^後遺症
いんちき
    ^^^^^後遺症
 ----------------------------------------------
 ################################################### 4
 ----------------------------------------------
 ----------------------------------------------
 ----------------------------------------------
 ----------------------------------------------
炎を吐き出す<==排他的動作
 ---------------------------------------------- 




マーティンはジョーの仕事を引き継ぎます...



ジョー:これは何ですか? このアプリケーションアーキテクチャですか? 図に1つのリンクがある2つの長方形。 プログラムのバージョンを作成したとき、ダイアグラムの最初に12個の長方形があり、アヒルが鳴くと飛ぶことができただけであり、上司が行動の50の異なる側面を思い付き、各側面にいくつかの異なる実装オプションがあったとき、300個以上ありました! 変数をパーマネントから分離し、柔軟性を実現し、コードの重複を排除するために、階層に従って作成したアーキテクチャの各グループに対して。 そして、あなたは、変化は一般に、アーキテクチャの境界の外で、匿名で不明確な領域に取り込まれ、そこに定数だけを残しました。 ただし、アプリケーションは少なくとも私のアプリケーションと同じくらい柔軟性と拡張性があります。 また、マガモと赤毛のアヒルを作成するとき、別の方法で同じような追加機能を実行できることを除いて、コードの重複はあまり見られませんが、これらは些細なことです。 このアプリケーションを非常に迅速に拡張すると思いますので、負荷を処理するための作業がまだ残っています。 気象ステーション用の気象モニターを作成してみませんか? 技術的な要件を満たしました。










All Articles