16 Sep 2017, 10:54

Java 8 で Command Pattern

はじめに

3年前の記事の補足です。

Java8で追加されたラムダ式でコマンドパターンを書き換えてみた。

かつては、Java 6を使っていたので、匿名クラスを利用して処理を実行と分離していた。

ポイント

  • インターフェースに 明示的に @FunctionalInterface アノテーションを追加する。
  • ラムダ式を使って、無名関数を渡す。

コード

19 Jun 2015, 12:29

Wrap-Unwrap Pattern についての覚書

Functional Python Programming という本を読んでいたら、 Wrap-Unwrap Pattern というものを知ったので、ちょっとメモ.

公式

unwrap(process(wrap(iterable)))
  • wrap() ラッパー
  • unwrap() アンラッパー ラッパーで処理したものをもとに戻す.
  • process() 処理したい手続き
  • iterable 処理したいデータ

ラッパーは、iterable なデータをタプルに加工する. タプルを利用するのは、タプルが immutable なデータ構造だから.

例: 最大値を求める

以下、python による例です.

以下のデータ構造で身長が最大の人を調べる.

  • 太郎: 170
  • 次郎: 160
  • 三郎: 180
students = [("Taro", 170), ("Jiro", 160), ("Saburo", 180)]

max 関数は、そのままでは利用できない.

>>> max(students)
('Taro', 170)

ラッパーでタプルを作成する. wrap 関数は、generator expression ともいう.

def wrap(students):
    return ((student[1], student) for student in students)

def unwrap(data):
    length, student = data
    return student

パターンを適用.

unwrap(max(wrap(students)))

>>> unwrap(max(wrap(students)))
('Saburo', 180)

その他

wrap 関数をいちいち定義するのは面倒なので、lambda が利用される.

>>> max(map(lambda s: (s[1], s), students))[1]
('Saburo', 180)

map を利用する場合の方が、generator expression よりも、高速らしい.

unwrap の操作でタプルの一番目、二番目を取り出すのは常套手段. なので、Haskell には、fst,snd という関数が用意されている.

  • fst: タプルの 1 番目の要素を取り出し
  • snd: タプルの 2 番目の要素を取り出し

24 Jan 2015, 15:23

関数型デザインパターンのプレゼン動画をまとめてみた

はじめに

オブジェクト指向言語の世界では, デザインパターンが人気!

関数型言語の世界でも, OO の影響を受けて, きっと誰かがパターンを考えているに違いないと考えて, いろいろとネットで情報収集してみた.

思ったとおりで, いくつか動画をみつけたのでまとめてみる.

Functinal programming patterns for the non-mathematician

15 分くらいに短くまとまっている動画. JavaScript.

関数に成り立つ法則によってまとめている.

  • Composition laws
  • Lenses laws
  • Fmap laws
  • Monad laws
  • Applicative laws
  • Monoid laws
  • Arrow laws

SlideShare のプレゼン資料

Functional Programming Patterns

動画はリンク先から.

プレゼン, 動画ともにとてもボリュームがある. 導入部の説明がとても笑える.

SlideShare のプレゼン資料

Patterns and Functional Programming

Patterns and Functional Programming from Chariot Solutions on Vimeo.

この本を書いた人の動画.

Amazon:

Pragmatic Bookshelf:

既存の OO Pattern を FP で置き換える.

こんな記事もみつけた.

Replacing Object Oriented Patterns
    Introduction
    Replacing Functional Interface
    Replacing State Carrying Functional Interface
    Replacing Command excerpt
    Replacing Builder For Immutable Object
    Replacing Iterator
    Replacing Template Method
    Replacing Strategy
    Replacing Null Object
    Replacing Decorator
    Replacing Visitor
    Replacing Dependency Injection

FP 独自のパターンも紹介.

Functional Patterns
    Introduction
    Tail Recursion excerpt
    Mutual Recursion
    Filter-Map-Reduce
    Chain of Operations
    Function Builder
    Memoization
    Lazy Sequence
    Focused Mutability
    Customized Control Flow
    Domain-Specific Language

これはあとで読みたい.(できれば日本語訳で!!)

Functional Design Patterns

Clojure による,パターンの紹介.

内容をみていないのだけれども, ブックマークだけしておく.

  • State/Event,
  • Consequences,
  • Accumulator
  • MapReduce,
  • Reduce/Combine,
  • Recursive Expansion,

おわりに

シンフォニーとミニマルミュージック

OO でのパターンと FP のパターンでは, うけるイメージが違った.

OO のパターンからは, 堅牢な構築物のようなイメージを受ける. それは, クラス図で表現されているからかもしれない.

それに対して, FP からは, ミニマルな文様なようなイメージを受ける. 微細なパターンが組み合わさって, 全体をつくるような. FP で言うところのパターンは小さいので, OO でいうところの idiom のようにもとらえられる.

それは, 堅牢な交響曲と, 微細なテクノミュージックのような違いを感じる.

今年の目標は関数型パターンをみにつけること.

去年の目標は, OO のデザインパターンを身につけることが目標だった.

今年は, FP のパターンを身につけることを目標にしよう.

それにしても, FP のパターンは Gof のような教科書が見当たらない.

動画の内容にも言えることだけれども, いろんなひとがそれぞれの意見を持っているような群雄割拠状態.

だれでもいいので, すごい本とか出してこの分野を統一してくれないかなと思ってみたり.

30 Dec 2014, 08:14

Gof デザインパターンの勉強メモ記事とブックマークまとめ

はじめに

今年の目標は, デザインパターンの攻略!

ということで, この一年でまとめた情報や書いた記事のブックマークページです.

過去記事は, Ruby と Java が入り混じっていたり, 気まぐれでどんどん更新していったので, とてもまとまりがないとおもう.

強引に一つのページにまとめてみた感じ… (*‘д`*)

一年の振り返り

Rubyist になる

去年の 12 月から Rubyist になった.

そして, オブジェクト指向開発に触れることになった.

楽しかったので, 何も考えずにデザインパターンを使ってみたりした. しかし, これはのちに後悔することになった.

保守性よりもスピードを重視するべきだったのに, デザインパターンをつかって, 仕事が遅れてしまったという悲しい思い出.

POSA を学ぶ

coursera で POSA (Pattern Oriented Software Archtecture) をまなんだ.

ここで登場した Schmidt さん (POSA2 を書いた人) がとても情熱的に Pattern を語るのをきき, パターンが素晴らしいと洗脳された.

Javaer になる

年の暮れごろに Javaer になった.

すると, 一緒に仕事する人にすごく怖くてパターンに詳しいひとがいた.

OO でまともに設計した経験のない自分は, その人からしてみれば素人なので, レビューしているのだか教育をされているのだかわからない感じだった.

そんなことだから考え方がいつも手続的なんだ と, しょっちゅう罵倒された../ (._.)

毎日怯えつつ, あんなやついつかギャフンと言わせてやるという, なかば憎悪に似た感情を抱きつつ??, パターンの勉強をしたのだった.

これからどうするか?

Gof デザインパターンは 20 年前のもの.

最近, おもしろい podcast を発見した.

Gang of Four のメンバが 20 年たった今, なにを感じているのかをインタビューした podcast.

Gof デザインパターンは, 20 年も前のものなのだ.

そして, 今や時代はオブジェクト指向から関数型へと向かっている.

OO から FP へ

デザインパターンを学習していると, 関数型だともっと簡潔にかける気がしてきた.

たとえば, これ.

というわけで, 来年は関数型言語を頑張る. 関数型言語のパターンみたいなものはあるのかな??

Pattern

パターン

建築環境に繰り返し現れる課題を解決に導く具体的な方策を記述したもの.

アレクサンダーのパターン形式

  • パターン名
  • 写真
  • 上位パターンへのつながり
  • 本文
  • 下位パターンへのつながり

パターンランゲージ

建築において繰り返し現れる構造を再利用しやすい形式でまとめたもの. あるいは, パターンを集めて一つの体系としてまとぬあげたもの.

建築家クリストファーアレクサンダーが考えた建築手法.

時を超える建築の道

パターンランゲージを記した建築理論. 本の題名.

角谷信太郎さんのおもしろいはなしの youtube 動画も見つけた.

無名の質

生き生きとした建築や街が備えている特性. 古い街並みに潜む住みやすさや美しさ.

Design Pattern

Gof Degign Pattern

生成に関するパターン

Factory の原則

生成と実装を分離することで, プログラムはシンプルになる.

  • 生成パラメータの指定方法をシンプルに
  • 生成後の管理をシンプルに
  • 生成するオブジェクトの指定方法をシンプルに

特定のケースで特定のオブジェクトを生成するのは手続き思考的.

2 つをわけて考えることで設計に集中.

  • 動作方法
  • 生成,管理方法

Factory Method

オブジェクトの生成を行う時のインタフェースを規定して, インスタンス化するクラスを決定するのはサブクラスに任せる.

factoryMethod の中でオブジェクトの生成をすることで, 生成を生成オブジェクト (メソッド) 内にカプセル化.

switch 文は Abstract Factory によってリファクタリング可能.

  1. C 言語での応用

    C 言語で Abstruct Data Type な設計をつかうときの常套手段.

    int createInstance (void) {
      return calloc (4);
    }
    
    int destroyInstance (int ptr) {
      free (ptr);
      return NULL;
    }
    
    int main (void) {
      int *instance = createInstance ();
      instance = destoryInstance (instance);
      return 0;
    }
    
  2. 使いどころ

    グループ化されたオブジェクトについて,

    • 生成用オブジェクト (Creator)
    • 振る舞い用オブジェクト (Product)

    のペアを作成するとき.

Abstract Factory

関連するオブジェクト群を, その具象クラスを明確にせずに生成するための インタフェースを提供する.

関連するインスタンス群を生成するための API を集約することによって, 複数のモジュール群の再利用を効率化することを目的とする.

実装は意識せずに, インタフェース (API) のみで, 抽象的な部品をつくりあげる.

Factory Method 自体のカプセル化. マルチ Factory Method. Factory Methods.

Builder

オブジェクトの生成手順が複雑な場合に, その生成過程をカプセル化する.

ドメイン駆動設計でいうところのエンティティオブジェクトを生成する Factory.

Prototype

生成するオブジェクトの原型をコピーして新しいオブジェクトを生成する.

Abstract Factory と似ている.

  • new でオブジェクトを生成すれば Abstract Factory.
  • clone をつかう場合の Prototype.

複製を作成するためのメソッドを用意する. といういたって単純なもの.

プロトタイプ が複製を担当し, それ以外の生成における操作をクライアントが 担っている.

Map にテンプレートを登録しておいて, 利用するときに複製する. バイナリデータをマップにいれておいて, キーとなる名前をつけて管理する, など.

Java には, Clonable インタフェースがある.

クラスの数をかなり減らすことができる.

  1. 利用シーン

    • Abstract Factory パターンでなされるように,

    クライアント・アプリケーションにおいて オブジェクトの生成者をサブクラスにすることを回避する

    • 標準的な方法 (例えば’new’) で新しいオブジェクトを作ることによる

    固有のコストが所与のアプリケーションにとって高すぎる時にそれを回避する.

Singleton

システム内で生成可能なインスタンス数をひとつだけに制限する.

一般的なシングルトンパターンの実装方法は以下.

  • static method
  • private な 定数に オブジェクトを保存
  • オブジェクトは getInstance () メソッドで取得

各 Factory の違い

デザインはしばしば,

  • 比較的に複雑でなく,
  • カスタマイズしやすく,
  • サブクラスを急速に増やす

ファクトリメソッドを用いるところから出発

一層の柔軟性が必要となる箇所が発見されるに伴い,より柔軟だが複雑な Abstract Factory, Prototype, Builder へと発達してゆく.

  1. Factory Method

    ファクトリのクライアントとなるオブジェクトが, ファクトリオブジェクトにインスタンスの生成を委譲する.

    • 親クラスである Creator クラスが子クラスである

    ConcreteCreator クラスにオブジェクトの生成を委ねる

    • Creator クラスと ConcreteCreator クラスとの関連である.
    • 継承
    • [オブジェクト生成] の抽象化にポイントを置いたパターン
  2. Abstract Factory

    親クラスであるファクトリが, 実際のオブジェクトの生成をサブクラスに委譲する

    • Client のインスタンスが ConcreteFactory のインスタンスにオブジェクトの生成を委ねる
    • オブジェクト同士の関連
    • 委譲
    • [関連するオブジェクト群をまとめて生成するための手順] の抽象化
  3. 参考:

構造に関するパターン

Adapter

インタフェースを変換することにより, インタフェースに互換性がない クラス同士を接続する.

単なるラッパークラスとも言える.

ラッパー方法は 2 つ.

ここでは, ConcurrentLinkedQueue と ConcurrentArrayQueue を 生成時に交換するために, Adapter をつかっている.

Bridge

クライアントがアクセスするクラス (インタフェース) と実装クラスを分離して, それぞれを独立に変更できるようにする.

オブジェクト指向のこころの本にとても詳しく書いてある.

これぞ, オブジェクト指向の本質! みたいな.

  • インタフェースを用いて設計する.

機能追加と機能実装の組み合わせ爆発を抑止することができる.

Composite

部分-全体階層を表現するために, クラスの木構造に組み立てる. 同一の クラスから派生したサブクラスを木構造のノードとし, クライアントは木構造の任意の部分を同一のインタフェースで扱える.

別名, フォルダパターン. フォルダには, フォルダとファイルがある. こっちの名前のがわかりやすいし, 覚えやすい.

Decorator

サブクラス化ではなく委譲により, クラスに新しい機能を追加する.

ポイントは, オブジェクトの委譲方法が,

  • 集約ではなくてコンポジション
  • 継承ではなくてコンポジション

LinkedList 構造.

Facade

複数のクラス群からなるサブシステムにアクセスするための, インタフェースを提供する.

facade とは, 正面という意味.

Proxy

オブジェクトへのアクセスをフックするための代理オブジェクトを提供する.

Proxy は英語で代理人.

Windows のデスクトップショートカットもプロキシ.

本物のオブジェクトにアクセスするまえにクッションを置くことで, そこに機能追加できる.代理プラスアルファの機能をもつ.

Flyweight

一度生成したインスタンスはプーリングしておき, 必要なときに取り出して使う.

シングルトンパターンは フライウェイトパターンと合わせて利用されることがおおい.

特徴は,

  • private な 変数に オブジェクトを保存.
  • オブジェクトが存在すれば, getInstance で渡す. オブジェクトが存在しなければ, オブジェクトを作成して getInstance で渡す.
  1. wikipedia から説明引用

    その時点で対象のインスタンスが生成されていない場合

    • 対象のインスタンスを新たに生成する.
    • 生成したインスタンスをプールする (言い換えると, メンバのコンテナオブジェクトに格納する).
    • 生成されたインスタンスを返す.

    対象のインスタンスが既に生成されていた場合

    • 対象のインスタンスをプールから呼び出す.
    • 対象のインスタンスを返す.

振る舞いに関するパターン

Command

動作を表現するオブジェクト. 動作とそれに伴うパラメータをカプセル化したもの.

  1. 特徴

    • 手続きに必要なパラメータの一時格納場所として便利.
    • 関数呼び出しのためのパラメータを集めて,

    後で使用するためにコマンドを保存しておくことができる.

    • 保存されたデータ構造に対する追加, 削除が可能になる.
    • コマンドの生成と実行のタイミングの分離.

Chain of Responsibility

責務を持たせたオブジェクトの Chain に 要求を渡していく.

要求は,

  • そのオブジェクトで処理できればそこで処理する
  • そのオブジェクトで処理できなければ, 次のオブジェクトに渡す.
  1. 参考記事:

Interpreter

文字列からなる構文を構文解析 (Interprete) し, 構文を表現したオブジェクト構造ともとの文字列を関連付ける.

Iterator

オブジェクトの集合 (データ構造, コンテナ) があるとき, その集合の内部構造はカプセル化したままで, 要素に対して順にアクセスする方法を提供する.

コンテナオブジェクトの要素を列挙する手段を独立させることによって, コンテナの内部仕様に依存しない反復子を提供することを目的とする.

言語でサポートしていることがおおい. 拡張 for 文, for-each 文などと呼ばれる.

自前で実装するよりも, 言語に頼るほうがよい.

  1. Java

    Collection フレームワークでは, 反復子が利用できる.

    List<Integer> list = LinkedList<Integer>
    for (int i; list) {
    System.out.println (i);
    }
    

    Iterator インタフェースを実装することで, 自前のクラスにイテレータを適用できる.

  2. Ruby

    Enumerable モジュールを Mix-in する.

  3. 参考:

Mediator

複数のオブジェクトを相互作用させる場合に, お互いのオブジェクト同士が直接参照することをなくすため, 相互作用そのものをオブジェクトとして定義する.

Memento

オブジェクトの状態を保存しておき, 元に戻せるようにしておく. オブジェクトを以前の状態に (ロールバックにより) 戻す能力を提供する.

Observer

あるオブジェクトに依存した複数のオブジェクトがある場合に, 被依存オブジェクトの状態変化を, 依存オブジェクトに通知する.

Ruby ではライブラリがある.

イベントリスナ.

State

状態に応じてオブジェクトの振る舞いを変更したいときに, 振る舞いを別オブジェクトにカプセル化する.

Strategy

アルゴリズムをカプセル化して, アルゴリズムを交換可能にする. ひとつの入力データに対して, アルゴリズム毎に異なる結果を出力する.

アプリケーションで使用されるアルゴリズムを動的に切り替える必要がある際に有用.

  • Android
  • Windows
  • Linux
  1. 変更を考慮して設計するアプローチ

    オブジェクト思考のこころより引用.

    • 変更内容を予測するのではなくて, どこに変更が発生するのかを予測する
    • 実装を用いてプログラミングするのではなくて, インタフェースを用いてプログラミンクする.
    • クラス継承よりも, オブジェクトの集約を多用する.
    • 流動的要素をカプセル化する.

    switch 文を多用したり, グチャグチャになってきたら赤信号. switch 文は流動的要素なので, その部分をクラスに分離してカプセル化する.

    クラスに分離する際は, 継承をさけて集約を多用する.

  2. Effective Java から

    p101 戦略を表現するために関数オブジェクトを使用する

    • 戦略を現すインタフェースを用意
    • 個々の具象戦略に関してそのインタフェースを実装しているクラスを定義.
      • 具象戦略が一度しか利用されないならば, 無名クラスで作成
      • 繰り返し利用されるならば, public static final の フィールド or static factory method を通じて提供.
  3. 名前のつけかた

    xxxStrategy

Template Method

単なる継承.

アルゴリズムを複数のステップに分解し, それぞれのステップを抽象メソッドにする. 各ステップでのメソッドの実装はサブクラスで定義する.

システムのフレームワークを構築するための手段としてよく活用される.

Factory Method パターンは, 内部に Template Method パターンを包含することが多い

class A
  def execute ()
    raise "to be implemented"
  end
end

class B < A
  def execute ()
  end
end

class C < A
  def execute ()
  end
end

Visitor

複数のオブジェクトからなるオブジェクト構造があるときに, それぞれのオブジェクト要素に処理を追加または オブジェクト要素の処理を変更するため, Visitor クラスを用意する.

OOP の 2 大原則

オブジェクト指向のこころとは, ズバリ以下だ.

  • 流動的要素を探し出してカプセル化する
  • クラス継承よりもオブジェクトの集約を多用する

デザインパターンとともに学ぶオブジェクト指向のこころより

カプセル化の視点で整理

カプセル化がデータ隠蔽というのは狭義の定義.

カプセル化とはあらゆるものを隠蔽すること.

  • データ
  • メソッド
  • 実装
  • 派生クラス
  • 設計の詳細
  • 実体化の規則

流動的要素を探し出してカプセル化する. 委譲は手段.

この観点から, デザインパターンをとらえ直すと,

流動的要素 Pattern


アルゴリズム Strategy 状態 State 振る舞い Decorator パターンマッチ, 型 Visitor 動作, 要求 Command 実装 Bridge 変化への反応 Observer 相互作用 Mediator 生成 Factory Method, Abstract Factory , Prototype 一意性 Singleton, Flyweight 構造の生成 Builder 集合の巡回構造 Iterator インタフェース Adapter システム Facade 設計の詳細 Template Method

Links

概要がかかれたページ

噛み砕かれた, わかりやすい説明.

図解で解説されている.

サンプルソースが豊富.

30 Dec 2014, 05:31

パターンは暗記するものではなく導くもの! オブジェクト指向のこころを読んだ読書メモ

はじめに

オブジェクト指向の本質を学ぶために, オブジェクト指向のこころを読みました.

読んだ理由

主な動機は,

  • パターンの本質を身につけるため.
  • パターンを適用するベースとなるオブジェクト指向を深く理解するため.

以下の記事の NextAction で選んだ本だった.

オブジェクト指向の本質を学べる本として, ネットで評判が高かったので購入.

また, 今年の目標としてデザインパターンを理解することをあげていたので, デザインパターンという言葉自体に惹かれた.

内容

OOP の 2 大原則とは?!

オブジェクト指向のこころとは, ズバリ以下だ.

  • 流動的要素を探し出してカプセル化する
  • クラス継承よりもオブジェクトの集約を多用する

この 2 つの概念は書くページの至る所に現れる. この 2 つの概念を説明するために, 他のページが書かれているといってもいい.

そして, これを実現するための手段がデザインパターンだ. 本質から導いた結果を,

“ほら, これが Gof では xxx パターンと名づけられているんだよ”

といわれる. これが デザインパターンとともに学ぶ という副題の意味.

Bridge Pattern に OO の本質あり

そして, この原則を解説するために, Bridge Pattern が詳解されている.

Bridge Pattern については, こちらも参照.

登場するパターン

すべてのパターンが出てくるわけではない. オブジェクト指向のこころを解説するための例として, パターンを引用しているに過ぎない.

  • Facade
  • Adapter
  • Bridge
  • Strategy
  • Abstract Factory
  • Decorator
  • Observer
  • Template Method
  • Singleton
  • Factory Method

パターンで考える

この本の独特な手法は, パターンで考える方法が載っていること.(第 13 章)

  • Facade
  • Adapter
  • Bridge
  • Abstract Factory

以下がその手順.

  • パターンの洗い出し … コンテキストに存在するパターンをまずは列挙.
  • パターンの分析・適用 ここで大事なのは, このコンテキストに置いて

    • “なにか問題なのか?”
    • “なにを解決したいのか?”

    をかんがえること. 1-4 を繰り返す. 1. パターンの並べ替え 2. パターンの選択と設計の拡張 3. 追加のパターンの洗い出し 4. 繰り返し

  • 設計の詳細

詳細は書籍を.

Java と UML の知識が必要

パターンを表現するために クラス図と Java のサンプルコードもふんだんに載っている.

そのため, 本の前半には, UML を解説するページも少し割かれている.

感想

なんども読み返した

読み始めたのは半年以上前. 興味がある部分からつまみ読みをはじめた.

一回読んでも大抵は忘れてしまう. この本には, オブジェクト指向設計とはなにかについて, 凝縮した言葉でかかれているため,同じページを何度も読み返した.

Web での評判は本物.これからも折に触れて読み返すことになるだろう.

パターンは暗記するものではなく, 導出するもの

この本を読む前, デザインパターンは暗記してすぐに思い出せるように しておく必要があると思っていた.

実はそうではなくて, 本質だけ押されておいて, 自然とデザインパターンは導き出すことが大事だと思った.

大学受験の数学も, ひたすら公式を暗記しても応用問題が解けない. 逆に理論的なところを抑えれば, 公式を暗記しなく立って導出できるし, 応用も効くようになる.

それと同じことかもしれない.

学習メモ

オブジェクト指向設計

名詞/ 動詞 抽出法

  • 問題領域に存在する名詞を洗い出し, それらを表現するオブジェクトを生成する.
  • 名詞に関連した動詞を洗い出し, それらを表現するメソッドを追加する

共通性/ 可変性 分析法

  • 共通性分析:時間が経っても変化しにくい構造を見つけるもの 共通性分析によってまとめられた概念を抽象クラスによって表現
  • 可変性分析:変化しやすい構造を洗い出すもの 可変性分析で得た流動的要素は抽象クラスの派生クラスによって実装される

設計手順:

  • (抽象クラス) このクラスが持つ責務をすべて全うするにはどうようなインターフェイスが必要か?
  • (派生クラス) この特定実装の中でどうのようにして与えられた仕様を実装できるのか?

Jim Coplien が提唱. p235 第 15 章から抜粋.

  1. Links

カプセル化の視点で整理

カプセル化がデータ隠蔽というのは狭義の定義.

カプセル化とはあらゆるものを隠蔽すること.

  • データ
  • メソッド
  • 実装
  • 派生クラス
  • 設計の詳細
  • 実体化の規則

流動的要素を探し出してカプセル化する. 委譲は手段.

この観点から, デザインパターンをとらえ直すと,

流動的要素 Pattern


アルゴリズム Strategy 状態 State 振る舞い Decorator パターンマッチ, 型 Visitor 動作, 要求 Command 実装 Bridge 変化への反応 Observer 相互作用 Mediator 生成 Factory Method, Abstract Factory , Prototype 一意性 Singleton, Flyweight 構造の生成 Builder 集合の巡回構造 Iterator インタフェース Adapter システム Facade 設計の詳細 Template Method

これからどうするか

暗記はやめよう

デザインパターンをただ暗記する態度は改めなければ.

本質から導けば, 覚える量も減るし, 応用も効くはず.

暗記ではなくて, 本質を繰り返し唱えること.

問題を考えよう

何か問題でパターンによってなにが改善されるかを考える癖をつけよう.

デザインパターンは, 概して複雑. 実際に仕事で利用するには, 工数 との兼ね合いを考えるようにする.

保守なんてされないだろうコードを, たくさん工数をかけてデザインパターンを駆使して書いても, 価値はない.

問題が保守性だとして, 保守する必要がなければパターンは適用する必要はない.

OO から距離をおいてみてみる

今年の目標は OO を理解することだったので, かなりの時間を OO に割いてきた.

来年の目標は関数型パラダイムを理解することなので, 一旦 OO の世界からは距離を置こうと思う.

OO と関数型を比較しつつ, 距離を追いてみることで, また見える世界が変わるかもしれない.

13 Dec 2014, 03:54

Adapter, Facade, Proxy パターンの違いのメモ

はじめに

Gof のデザインパターンで Adapter, Facade, Proxy があり, 違いがわからなかったので, 整理してみた.

まずは定義から

Adapter

インタフェースを変換することにより, インタフェースに互換性がない クラス同士を接続する.

既存のクラスに対して修正を加えることなく, インタフェースを変更することができる.

継承を利用する場合と委譲を利用する場合がある.

Facade

複数のクラス群からなるサブシステムにアクセスするための, インタフェースを提供する.

異なるサブシステムを単純な操作だけを持った Facade クラスで結び, サブシステム間の独立性を高める事を目的とする.

facade とは, 正面という意味.

Proxy

オブジェクトへのアクセスをフックするための代理オブジェクトを提供する.

Proxy は英語で代理人.

ラッパー

ラッパーという概念がある.

あるクラスや関数, データ型などが提供する機能やデータを含み, 別の形で提供するもののこと.

どれもラッパーと言える.

オブジェクト思考のこころより

オブジェクト思考のこころという本に, Adapter と Facade の比較表がある.

以下, Proxy パターンも交えて整理すると,

  • Facade はインタフェースを簡素化する
  • Adapter は既存インタフェースを他のインタフェースに変換する
  • Proxy はインタフェースを変更せずに機能追加する.

                                                    Facade   Adapter   Proxy
    

    既存クラスがある? ○ ○ ○ インタフェースを再設計する? × ○ × ボリモーフィズムによるオブジェクトの振る舞いが必要? × ○ × より簡素なインタフェースが必要? ○ × ×

コードでの例

class Target {
    void printInt (int i) {
        System.out.println (i);
    }

    void printLong (long l) {
        System.out.println (l);
    }
}

class Adapter {
    Target target;
    Adapter (Target target) {
        this.target = target;
    }

    void printInt (Integer i) {
        target.printInt (i);
    }

    void printLong (Long l) {
        target.printLong (l);
    }

}

class Facade {
    Target target;
    Facade (Target target) {
        this.target = target;
    }

    void print (long l) {
        target.printLong (l);
    }
}

class Proxy {
    Target target;
    int intCount = 0;
    int intCache= 0;
    long longCount = 0;
    long longCache = 0; 

    Proxy (Target target) {
        this.target = target;
    }

    void printInt (Integer i) {
        target.printInt (i);
        intCount++;
        intCache = i;
    }

    void printLong (Long l) {
        target.printLong (l);
        longCount++;
        longCache = l;
    }
}

10 Dec 2014, 15:56

Factory Method と Abstract Factory の違いを順に理解する

はじめに

デザインパターンにでてくる Factory Method と Abstract Factory.

なんだか, いつになっても違いが分からない… というわけで一旦整理してみることにした.

能書き

まずは, 一般的な説明をネットからひろう.

Factory の原則

生成と実装を分離することで, プログラムはシンプルになる.

  • 生成パラメータの指定方法をシンプルに
  • 生成後の管理をシンプルに
  • 生成するオブジェクトの指定方法をシンプルに

特定のケースで特定のオブジェクトを生成するのは手続き思考的.

2 つをわけて考えることで設計に集中.

  • 動作方法
  • 生成,管理方法

Factory Method

オブジェクトの生成を行う時のインタフェースを規定して, インスタンス化するクラスを決定するのはサブクラスに任せる.

factoryMethod の中でオブジェクトの生成をすることで, 生成を生成オブジェクト (メソッド) 内にカプセル化.

Abstract Factory

関連するオブジェクト群を, その具象クラスを明確にせずに生成するための インタフェースを提供する.

関連するインスタンス群を生成するための API を集約することによって, 複数のモジュール群の再利用を効率化することを目的とする.

実装は意識せずに, インタフェース (API) のみで, 抽象的な部品をつくりあげる.

Factory Method 自体のカプセル化. マルチ Factory Method. Factory Methods.

両者の違い

Factory Method

ファクトリのクライアントとなるオブジェクトが, ファクトリオブジェクトにインスタンスの生成を委譲する.

  • 親クラスである Creator クラスが子クラスである

ConcreteCreator クラスにオブジェクトの生成を委ねる

  • Creator クラスと ConcreteCreator クラスとの関連である.
  • [オブジェクト生成] の抽象化にポイントを置いたパターン

Abstract Factory

親クラスであるファクトリが, 実際のオブジェクトの生成をサブクラスに委譲する

  • Client のインスタンスが ConcreteFactory のインスタンスにオブジェクトの生成を委ねる
  • オブジェクト同士の関連
  • [関連するオブジェクト群をまとめて生成するための手順] の抽象化

コードから理解する

能書きはいくら読んでもわからない.

というわけで, コードから理解する.

Factory Method

パターン未適用

まずは基本から. if-else が汚いことがよくわかる.

public class FactoryMethodSample {
    enum Type { FIRST, SECOND }

    public static void main (String[] args) {
        Type type;
      Alphabet alphabet;

        type = Type.FIRST;
        if (type == Type.FIRST) {
            alphabet = new A ();
        }
        else {
            alphabet = new B ();
        }
        alphabet.whoAreYou ();

        type = Type.SECOND;
        if (type == Type.FIRST) {
            alphabet = new A ();
        }
        else {
            alphabet = new B ();
        }
        alphabet.whoAreYou ();
    }
}

abstract class  Alphabet {
    abstract void whoAreYou ();
}

class A extends  Alphabet {
    void whoAreYou () { System.out.println ("I'm A");}
}

class B extends  Alphabet {
    void whoAreYou () { System.out.println ("I'm B");}
}

static ファクトリーメソッド適用

Factory の原則にしたがうと, 生成部分と振る舞いをわけることがシンプルなコードへの第一歩.

ということで, 生成部分をサブメソッドに抽出する.

これを, Effective Java では, static ファクトリーメソッドといっている.

public class StaticFactoryMethodSample {
    enum Type { FIRST,SECOND }

    public static void main (String[] args) {
        Type type;
        Alphabet alphabet;

        type = Type.FIRST;
        alphabet = factoryMethod (type);
        alphabet.whoAreYou ();

        type = Type.SECOND;
        alphabet = factoryMethod (type);
        alphabet.whoAreYou ();
    }

    static Alphabet factoryMethod (Type type) {
        if (type == Type.FIRST) {
            return new A ();
        }
        else {
            return new B ();
        }
    }
}

abstract class Alphabet {
    abstract void whoAreYou ();
}

class A extends Alphabet {
    void whoAreYou () { System.out.println ("I'm A");}
}

class B extends Alphabet {
    void whoAreYou () { System.out.println ("I'm B");}
}

ファクトリーメソッド パターン適用

そして, これがファクトリーメソッド パターン適用版. static ファクトリメソッドをオブジェクトに抽出.

抽象クラスに生成メソッドを定義して, サブクラスで実装する.

if-else 文が消滅しているところに注目.

public class FactoryMethodSample {
    public static void main (String[] args) {
        Creator creator;
        Alphabet alphabet;

        creator = new CreatorA ();
        alphabet = creator.factoryMethod ();
        alphabet.whoAreYou ();

        creator = new CreatorB ();
        alphabet = creator.factoryMethod ();
        alphabet.whoAreYou ();
    }
}

abstract class Creator {
    abstract Alphabet factoryMethod ();
}

class CreatorA extends Creator {
    Alphabet factoryMethod () {
        return new A ();
    }
}

class CreatorB extends Creator {
    Alphabet factoryMethod () {
        return new B ();
    }
}

abstract class Alphabet {
    abstract void whoAreYou ();
}

class A extends Alphabet {
    void whoAreYou () { System.out.println ("I'm A");}
}

class B extends Alphabet {
    void whoAreYou () { System.out.println ("I'm B");}
}

Abstract Factory

つぎに, Abstract Factory は Factory Method のカプセル化に過ぎないことを示す.

Factory Method を発展させたのが, Abstract Factory.

ファクトリメソッドのソースに Number という概念を加える. Alphabet と Number には関係がある.

Abstract Factory は 関連ある複数のオブジェクトの生成のための API をひとつのオブジェクトに集約する.

パターン未適用

まずは, 汚いコードから.

public class AbstractFactorySample {
    enum Type { FIRST,SECOND }

    public static void main (String[] args) {
        Type type;
        Alphabet alphabet;
        Number number;

        type = Type.FIRST;
        if (type == Type.FIRST) {
            alphabet = new A ();
        }
        else {
            alphabet = new B ();
        }

        if (type == Type.FIRST) {
            number = new One ();
        }
        else {
            number = new Twe ();
        }
        alphabet.whoAreYou ();
        number.whoAreYou ();

        type = Type.SECOND;
        if (type == Type.FIRST) {
            alphabet = new A ();
        }
        else {
            alphabet = new B ();
        }

        if (type == Type.FIRST) {
            number = new One ();
        }
        else {
            number = new Twe ();
        }
        alphabet.whoAreYou ();
        number.whoAreYou ();
    }
}

abstract class Alphabet {
    abstract void whoAreYou ();
}

class A extends Alphabet {
    void whoAreYou () { System.out.println ("I'm A");}
}

class B extends Alphabet {
    void whoAreYou () { System.out.println ("I'm B");}
}

abstract class Number {
    abstract void whoAreYou ();
}

class One extends Number {
    void whoAreYou () { System.out.println ("I'm 1");}
}

class Twe extends Number {
    void whoAreYou () { System.out.println ("I'm 2");}
}

ファクトリメソッドパターン適用

ここで, まずはファクトリメソッドを適用して整理する.

public class AbstractFactorySample2 {

    public static void main (String[] args) {
        AlphabetCreator alphabetCreator;
        NumberCreator numberCreator;        
        Alphabet alphabet;
        Number number;

        alphabetCreator = new CreatorA ();
        numberCreator = new CreatorOne ();
        alphabet = alphabetCreator.factoryMethod ();
        number = numberCreator.factoryMethod ();
        alphabet.whoAreYou ();
        number.whoAreYou ();

        alphabetCreator = new CreatorB ();
        numberCreator = new CreatorTwe ();
        alphabet = alphabetCreator.factoryMethod ();
        number = numberCreator.factoryMethod ();
        alphabet.whoAreYou ();
        number.whoAreYou ();
    }
}

abstract class AlphabetCreator {
    abstract Alphabet factoryMethod ();
}

class CreatorA extends AlphabetCreator {
    Alphabet factoryMethod () {
        return new A ();
    }
}

class CreatorB extends AlphabetCreator {
    Alphabet factoryMethod () {
        return new B ();
    }
}

abstract class NumberCreator {
    abstract Number factoryMethod ();
}

class CreatorOne extends NumberCreator {
    Number factoryMethod () {
        return new One ();
    }
}

class CreatorTwe extends NumberCreator {
    Number factoryMethod () {
        return new Twe ();
    }
}

abstract class Alphabet {
    abstract void whoAreYou ();
}

class A extends Alphabet {
    void whoAreYou () { System.out.println ("I'm A");}
}

class B extends Alphabet {
    void whoAreYou () { System.out.println ("I'm B");}
}

abstract class Number {
    abstract void whoAreYou ();
}

class One extends Number {
    void whoAreYou () { System.out.println ("I'm 1");}
}

class Twe extends Number {
    void whoAreYou () { System.out.println ("I'm 2");}
}

Type と if-else 文が取り除かれてすっきり. しかし, まだ冗長なところがある.

そこで, alphabetCreator と NumberCreator をひとつにまとめる.

Abstract Factory パターン適用

public class AbstractFactorySample3 {

    public static void main (String[] args) {
        Creator creator;
        Alphabet alphabet;
        Number number;

        creator = new FirstCreator ();
        alphabet = creator.alphabetFactoryMethod ();
        number = creator.numberFactoryMethod ();
        alphabet.whoAreYou ();
        number.whoAreYou ();

        creator = new SecondCreator ();
        alphabet = creator.alphabetFactoryMethod ();
        number = creator.numberFactoryMethod ();
        alphabet.whoAreYou ();
        number.whoAreYou ();
    }
}

abstract class Creator {
    abstract Alphabet alphabetFactoryMethod ();
    abstract Number numberFactoryMethod (); 
}

class FirstCreator extends Creator {
    Alphabet alphabetFactoryMethod () {
        return new A ();
    }
    Number numberFactoryMethod () {
        return new One ();
    }
}

class SecondCreator extends Creator {
    Alphabet alphabetFactoryMethod () {
        return new B ();
    }
    Number numberFactoryMethod () {
        return new Twe ();
    }
}

abstract class Alphabet {
    abstract void whoAreYou ();
}

class A extends Alphabet {
    void whoAreYou () { System.out.println ("I'm A");}
}

class B extends Alphabet {
    void whoAreYou () { System.out.println ("I'm B");}
}

abstract class Number {
    abstract void whoAreYou ();
}

class One extends Number {
    void whoAreYou () { System.out.println ("I'm 1");}
}

class Twe extends Number {
    void whoAreYou () { System.out.println ("I'm 2");}
}

おわりに

本をよんでも分かりにくいことは, より単純な例に落とし込めば自分でも理解できる.

Abstract Factroy と Factory メソッドの関係が分かって, スッキリ.

08 Dec 2014, 15:49

状態ありはプロトタイプパターンで, 状態なしはファクトリメソッドで実装 (Java)

はじめに

だんだん, タイトルが毎回同じになってきた.

今回はプロトタイプパターンの実装を Java で実施してみた.

Prototype パターン

生成するオブジェクトの原型をコピーして新しいオブジェクトを生成する.

Abstract Factory と似ている.

  • new でオブジェクトを生成すれば Abstract Factory./ Factory Method.
  • clone をつかう場合の Prototype.

複製を作成するためのメソッドを用意する. といういたって単純なもの.

プロトタイプ が複製を担当し, それ以外の生成における操作をクライアントが 担っている.

Map にテンプレートを登録しておいて, 利用するときに複製する. バイナリデータをマップにいれておいて, キーとなる名前をつけて管理する,など.

(実際に仕事では, バイナリのパケットをテンプレートから生成する処理につかった)

メリット

インスタンスのコンストラクタ引数で差分を渡すことで, クラスの数をかなり減らすことができる.

Java には, Clonable インタフェースがある.

利用シーン

  • Abstract Factory パターンでなされるように,

クライアント・アプリケーションにおいて オブジェクトの生成者をサブクラスにすることを回避する

  • 標準的な方法 (例えば’new’) で新しいオブジェクトを作ることによる

固有のコストが所与のアプリケーションにとって高すぎる時にそれを回避する.

サンプルコード

import java.util.HashMap;
import java.util.Map;

public class PrototypeSample {
    public static void main (String args[]) {
        printerFacotry factory = new printerFacotry ();
        Printer printer;

        printer = factory.create ("type a");
        printer.printMessage ();
        printer = factory.create ("type b");
        printer.printMessage ();
        printer = factory.create ("type c");
        printer.printMessage ();
    }
}

class Printer implements Cloneable {
    String str;

    public Printer (String str) {
        this.str = str;
    }

    public void printMessage () {
        System.out.println (str);
    }

    @Override
    public Printer clone () {
        Printer cloned = null;
        try {
            cloned = (Printer) super.clone ();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace ();
        }
        return cloned;
    }
}

class printerFacotry {
    Map<String, Printer> protoMap; 

    public printerFacotry (){
        protoMap = new HashMap<String, Printer>();
        protoMap.put ("type a", new Printer ("a"));
        protoMap.put ("type b", new Printer ("b"));
        protoMap.put ("type c", new Printer ("c"));
    }

    public Printer create (String type) {
        return protoMap.get (type).clone ();
    }
}

状態をもたないならば, プロトタイプは不要

無名クラスをクローンできるのかと思ったが, できなかった. そもそも, 無名クラスは状態を持たないので, クローンする必要がなかった.

この比較から以下のことが分かる.

  • 状態をもつオブジェクトをコピーするのはプロトタイプパターンが有用.
  • 状態をもたないオブジェクトは new で生成する ファクトリメソッドパターンが有用.

クロージャをわたす

Map のなかにクロージャを入れて, 好きな時に取り出すようにした.

これはけっこういいパターンかもしれない. 個人的に気に入った.

import java.util.HashMap;
import java.util.Map;

public class PrototypeSample {
    public static void main (String args[]) {
        printerFacotry factory = new printerFacotry ();
        Printer printer;

        printer = factory.create ("type a");
        printer.printMessage ();
        printer = factory.create ("type b");
        printer.printMessage ();
        printer = factory.create ("type c");
        printer.printMessage ();
    }
}

interface Printer {
    public void printMessage ();
}

class printerFacotry {
    Map<String, Printer> protoMap; 

    public printerFacotry (){
        protoMap = new HashMap<String, Printer>();
        protoMap.put ("type a", new Printer (){
            public void printMessage () { System.out.println ("a"); }
        });
        protoMap.put ("type b", new Printer (){
            public void printMessage () { System.out.println ("b"); }
        });
        protoMap.put ("type c", new Printer (){
            public void printMessage () { System.out.println ("c"); }
        });
    }

    public Printer create (String type) {
        return protoMap.get (type);
    }
}

クロージャ + 引数

さらに改良.

クロージャを Map に保存しておいて, 呼び出し時に外部から引数を与えるようにした.

これで, さらにメソッドが柔軟になった.

interface Printer {
    public void printMessage (String str);

}

class printerFacotry {
    Map<String, Printer> protoMap; 
    public printerFacotry (){
        protoMap = new HashMap<String, Printer>();
        protoMap.put ("type a", new Printer (){
            public void printMessage (String str) { System.out.println ("**"+str+"**"); }
        });
        protoMap.put ("type b", new Printer (){
            public void printMessage (String str) { System.out.println ("++"+str+"++"); }
        });
        protoMap.put ("type c", new Printer (){
            public void printMessage (String str) { System.out.println ("=="+str+"=="); }
        });
    }

    public Printer create (String type) {
        return protoMap.get (type);
    }
}

もはや, プロトタイプパターンの記事ではなくなっているが….

08 Dec 2014, 13:52

Java で Iterator Pattern を実装してみた

はじめに

最近, Gof のデザインパターンをすべて記事にしようと考えている.

そんなわけで, 今日は Iterator パターン. あまりに基本すぎて, 記事にするほどではないがするが…

Iterator パターンとは

オブジェクトの集合 (データ構造, コンテナ) があるとき, その集合の内部構造はカプセル化したままで, 要素に対して順にアクセスする方法を提供する.

コンテナオブジェクトの要素を列挙する手段を独立させることによって, コンテナの内部仕様に依存しない反復子を提供することを目的とする.

言語でサポートしていることがおおい. 拡張 for 文, for-each 文などと呼ばれる.

自前で実装するよりも, 言語に頼るほうがよい.

Java

Collection フレームワークでは, 反復子が利用できる.

List<Integer> list = LinkedList<Integer>
for (int i; list) {
System.out.println (i);
}

Iterator インタフェースを実装することで, 自前のクラスにイテレータを適用できる.

Iterator interface を実装する.

Iterator interface を実装した.

import java.util.Iterator;

public class IteratorSample {
    public static void main (String args[]) {
        myList list = new myList (1, new myList (2, new myList (3, null)));

        while (list.hasNext ()) {
            list.show ();
            list = list.next ();
        } 
        list.show ();
    }
}

class myList implements Iterator {
    myList next;
    int value;

    public myList (int value, myList next) {
        this.value = value;
        this.next = next;
    }

    @Override
    public boolean hasNext () {
        return (next != null);
    }

    @Override
    public myList next () {
        return next;
    }

    @Override
    public void remove () {
        // nop
    }

    public void show () {
        System.out.println (value);
    }
}

自前で hasNext, next メソッドを用意するよりも, Iterator interface を実装したほうが, 通に思われるというメリットがある.

関数型 Iterator パターン

関数型 Iterator パターンみたいなものを考えた. というよりも List の実装.

  • while を利用するのではなくて, 再帰を利用する.
import java.util.Iterator;

public class IteratorSample2 {
    public static void main (String args[]) {
        myList list = new myList (1, new myList (2, new myList (3, null)));
        whileLoop (list);
    }

    static void whileLoop (myList list) {
        list.show ();
        if (!list.hasNext ())
            return;
        else { 
            whileLoop (list.next ());
        }
    }
}

class myList implements Iterator<myList> {
    myList next;
    int value;

    public myList (int value, myList next) {
        this.value = value;
        this.next = next;
    }

    @Override
    public boolean hasNext () {
        return (next != null);
    }

    @Override
    public myList next () {
        return next;
    }

    @Override
    public void remove () {
        // nop
    }

    public void show () {
        System.out.println (value);
    }
}

なんか, 再帰のほうがいいな.おそまつさまでした.

17 Nov 2014, 14:25

Java のユーティリティクラスはシングルトンパターン/ フライウェイトパターンで実装する

はじめに

今日から仕事ではじめて Java の開発をはじめることになったので, とてもうれしい.

Java の常識あるあるのなかに, ユーティリティクラスというものがある. みんな当たり前のように話しているけれども, 自分は知らないので調べた.

ユーティリティクラスとは

以下の特徴をもつ.

  • いろんなところで繰り返し利用される処理をまとめたクラス.
  • インスタンス化されない.
  • すべてのメソッドは static で宣言される.
  • 状態を持たない処理をユーティリティにすることが多い.

C 言語 とかでよくライブラリと呼ばれているのを見かけたことがある. オブジェクト指向っぽくない. そんなことを考えたら, 同じような意見をちらほら発見した.

実現方法

メソッドには static をつける

static をつけることで, クラスをインスタンスすることなく メソッドを利用できる.

class SampleSingleton {
    static class SampleUtil {
        private SampleUtil () {} // インスタンス化を禁止するテクニック
        public static int plus (int x, int y) { return x + y; }
        public static int minus (int x, int y) { return x - y; }        
    }
    public static void main (String[] args) {
        System.out.println (SampleUtil.plus (1,1));
        System.out.println (SampleUtil.minus (2,1));        
    }
}

状態をもつ場合は, シングルトンパターンを適用する

なにかの値を保持する場合は, デザインパターンであるシングルトンパターンを利用する.

一般的なシングルトンパターンの実装方法は以下.

  • private な 定数に オブジェクトを保存
  • オブジェクトは getInstance () メソッドで取得

フライウェイトパターンで改良する

シングルトンパターンは フライウェイトパターンと合わせて利用されることがおおい.

特徴は,

  • private な 変数に オブジェクトを保存.
  • オブジェクトが存在すれば, getInstance で渡す. オブジェクトが存在しなければ, オブジェクトを作成して getInstance で渡す.

実装例

class SampleSingleton {
    static class SingletonUtil {
        private static final SingletonUtil INSTANCE = new SingletonUtil ();

        public static SingletonUtil getInstance (){ return INSTANCE; }
        public static int plus (int x, int y) { return x + y; }
        public static int minus (int x, int y) { return x - y; }        
    }


    public static void main (String[] args) {
        System.out.println (SingletonUtil.getInstance ().plus (1,1));
        System.out.println (SingletonUtil.getInstance ().minus (2,1));      
    }
}

または, Enum を利用するとという手もある. Effective Java の本では, この方法を推奨している.

class SampleSingleton {
    static public enum EnumUtil {
        INSTANCE;

        public static int plus (int x, int y) { return x + y; }
        public static int minus (int x, int y) { return x - y; }        
    }

    public static void main (String[] args) {
        System.out.println (EnumUtil.INSTANCE.plus (1,1));
        System.out.println (EnumUtil.INSTANCE.minus (2,1));     
    }
}

Special Thanks