忍者ブログ
[100]  [99]  [98]  [97]  [96]  [95]  [94]  [93]  [92]  [91]  [90
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

読みました。
ジョシュア・ケリーエブスキー 著 『パターン指向リファクタリング入門』
名著ということだったので。

そもそもリファクタリングなるものが一体何なのかよく分からないまま読み始めた。
リファクタリングとは既存のコードについて、「可読性や再利用性、独立性を高めること」だと、とりあえず今はそのように解釈しているつもり。
逆に、リファクタリングが必要なコードとは、「重複しており、不明確で、複雑な」コードだそうだ。そのようなコードに対して、デザインパターンを目指すべき一つのゴールとしてリファクタリングを行おう、という本のようです。

リファクタリングの本なので、既に書かれたコードをどうにかする事が主目的になる。例題のコードが仕分けられていく様子を逐一追っかけるのは骨が折れそうだったので、要点と思われるところにざっと目を通し、以下にメモ。
必要になったらちゃんと読みます。


■Creation Methodによるコンストラクタの置き換え
Creation Methodは本書で新たに定義したパターンとのこと。
コンストラクタをオーバーロードする代わりに、インスタンス生成方法を連想しやすい名前のメソッド(Creation Method)によりインスタンスを生成する。引数の型が全く同じメソッドを複数定義できる。
Creation Methodはstaticメソッドに限定されるものではない。
一つのクラス内にあまりにCreation Methodが多くなった場合には、インスタンスの生成方法の傾向別にFactoryクラスとして分離してもよい。重視するのはクラスの役割とメソッドの機能を明確にすること。

■Factoryによる生成処理の書き換え
Clientから直接インスタンスが生成出来ないような状況下で、中間で手続きするオブジェクト間でインスタンス生成に関わる情報のやりとりが発生しているような場合に、「解決策の散在」を予防する。
インスタンス生成のための情報の流れを1方向に限定し、情報をFactoryに集約させた上でインスタンス生成を行うように書き換える。
本書においては「1つ以上のCreation Methodを実装しているクラスがFactoryである」とのこと。

■Factoryによるクラス群の隠蔽
複数のクラスが共通のAPIを実装していて、かつ個々のクラス特有の機能にアクセスする必要が無いとき(またはそのような機能を有していないとき)、それらクラスのインスタンスを生成して基底クラス(interface)としてClientに渡すFactoryを作ることで、Clientにクラスの具体的な実装を知らせる必要がなくなる。
あるいは、ClientはFactoryのCreation Methodの名前から、得られたインスタンスの具体的な機能を類推することが出来る。
「インタフェースに対してプログラミングするのであって、実装に対してプログラミングするのではない」という原理の元、とのこと。

■Factory Methodによるポリモーフィックな生成の導入
あるサブクラス間でオーバライドしたFactory Methodが生成して返すインスタンスの実装のみが異なるような、類似したロジックになる場合に適用。
スーパクラスと該当のサブクラス群の間に抽象クラスを挿入し、Factory Method中のインスタンス生成箇所を抽出した抽象メソッドを定義し、類似メソッドはその抽象メソッドを呼び出すように書き換える。
するとサブクラスが目的のインスタンス生成のために実装すべき箇所が明確になる。

■BuilderによるCompositeの隠蔽
Compositeパターンは再帰的構造をつくるんだった。
Compositeを作ろうとすると、Compositeインスタンスの生成と親Compositeへの追加を適切に行う必要があり、めんどい。
ClientとBuilderクラスでComposite実装をカプセル化し、Composite生成と構成の処理をより単純な処理に置き換える。そうするとミスが減り、またClientとCompositeのインタフェースを切り離すことができる。

■Singletonのインライン化
「Singleton依存症患者自助グループ」が必要になるくらいSingleton依存症は深刻なものらしい。
インスタンスをグローバルにする必要がある状況というのはほとんど無いそうなので、別クラスのフィールドから当該クラスのインスタンス(当然Singletonは削除する)を参照するようにすれば済む。

■メソッドの構造化
「Composed Method」なる、他のメソッドの呼び出しを集めたメソッドの多用によって、可読性の高いコードにする。Composed Methodから呼び出されるメソッドは、互いに同程度の詳細レベルのものが好ましいとされる(処理の対象となるまとまりの大きさが同じ、ってことでいいのかな?)。
理想的には全ての処理がメソッドの呼び出しで行えるようになるまで、「メソッドの抽出」必要に応じ「クラスの抽出」を繰り返す(5~10行)。使われていないコード、重複しているコードは除去する。
意図が明確に伝わるメソッド名が重要。

■Strategyによる条件判断の置き換え
条件分岐によるロジック分岐を、各条件に対応するStrategyサブクラスのメソッドに委譲することで単純化を図る。
委譲先メソッドの引数として、元のクラス(Context)のインスタンスを渡す場合と、必要なパラメータを渡す場合とが考えられる。前者においてはStrategyが必要とするContextのメンバを外部に公開する必要が出てくる。後者ではインタフェースの引数に、Strategyサブクラスで必ずしも必要とされないパラメータを含む場合が生じる。
これらの善し悪しを鑑み、場合によっては条件に対応したContext継承クラスで解決する方針も考慮に入れる。

■Decoratorによる拡張機能の書き換え
条件に応じた機能の拡張(フラグが立ってたらこのコードも実行してネ)を、Decoratorパターンで書き換える。同じDecoratorインタフェースの実装クラス間で互いに互いをラップ出来るようにする。
Clientは装飾されたクラスと装飾の対象とされたクラスが同じインタフェースを有しているので、実装を気にすることなく元のクラスと同じように使用出来る。一方で、オブジェクトの型に依存するようなコードは書き直す必要がある。
複数のDecoratorインタフェース実装同士は互いに独立していることが望ましい。(「顔にどんなメイクをしていても全てのドレスが着られるようにすべき。」というような意味かなあ。)それが難しい場合にはDecorator群をカプセル化し、Creation Methodで適切な組み合わせで装飾できるようにすべき。
Decortorが何の機能に対する拡張(装飾)なのか、明確になるように適用すべき。
将来的に機能拡張を拡張する可能性が低いなら、わざわざ機能を散逸させる必要は無いのかもしれない。

■Stateによる状態変化のための条件判断の置き換え
StrategyとStateとの違いは、条件分岐を置き換えるにあたっての動機が、前者は処理の委譲による単純化が主であるのに対し、後者は各条件(State)間の遷移しやすさが主になる。

■Compositeによる暗黙的なツリー構造の置き換え
暗黙的というのはオブジェクト同士の関係のようには外に表れないということ。例えばXMLテキストとか、階層状になった条件ロジックにより生成されるデータとか。
ツリー状のデータ構造を生成・編集する際に、データ・条件ロジックの各種ノードに対応したCompositeサブクラス群を利用することで、データに対する直接操作をカプセル化する。
Clientが最終的に得るデータ構造は変わらないが、構築作業が単純になる。
元々のデータ構造が単純であれば、かえって設計が複雑になるだけ。

■Commandによる条件付きディスパッチャの置き換え
条件付きディスパッチャとは、受け取ったリクエストを振り分け適切な処理を行うための条件文を指す。
リクエストの振り分け条件の変更を動的にしたい場合や、条件付きディスパッチャのコードが肥大化している場合に、Commandパターンが有効となる。
各Commandクラスは対応する条件が明確なクラス名と、適切な処理を呼び出すためのメソッドを持っていればよい。条件付きディスパッチャを担っていたクラス(Invoker)は、Command基底クラスの参照フィールドを持ち、条件による振り分けと処理の実行をカプセル化する。
条件が静的でも問題なく、分岐も少ない場合はリファクタリングの必要はない。

■Template Methodの形成
抽象メソッドの実装間で内部のロジックが似通った内容になっている場合に、共通する部分を基底抽象クラスの具象メソッドとして抽出し、ロジックの異なる箇所を抽象メソッドに置き換える。これによりサブクラスで実装すべきロジックが、より限定されカスタマイズが容易になる。
サブクラスの機能はTemplate Methodの実装に完全に依存する事になるので、多くの場合ドキュメントが必要になる。また、Template Methodは必要に応じてfinal宣言をする。

■Compositeの抽出
ある共通の基底クラス(ただしCompositeではない)を持つCompositeクラス(つまり互いに別のツリー構造を表現していることになる)の間でコードの重複が発生する場合に、重複するコードを抽出してそれらCompositeの共通Compositeスーパクラスを新たに作る。リファクタリング対象となったクラス群は、新しいCompositeのサブクラス群になる。
このとき、新しいCompositeとして抽出される処理をツリー構造(自分の子)に関わるものに限定することで、処理対象となる構造がより一般化され再利用しやすくなる。

■Compositeによる単数・複数別の処理の置き換え
データやロジックを単数・複数(Collectionとか)別にインタフェース(シグネチャ)を分けていたものを、Compositeパターンを利用する事でその区別を無くし窓口を統一する。
メソッドが再帰的に呼び出されることによって、複数オブジェクトに対するより柔軟で豊富な処理が可能になる。(例として条件分岐ツリーをCompositeにして、動的な条件ロジック生成および処理結果のマージを行っている。)

■Observerによるハードコードされた通知の置き換え
ハードコーディング って何よ。どうやら、互いの実装に依存し合うクラス間の関係を指すみたい。
新しい情報の通知をやりとりするNotifier・Recieverオブジェクト間のハードコーディングによる密な結合を、より疎にしたい場合に行うリファクタリング。
特定の通知のために個別に作成されていたNotifier・Recieverをより汎化する。NotifierはObserverインタフェース実装クラスに。各Notifierをそれぞれ個別に参照していたRecieverは削除し、Observerインタフェースを参照するクラスに置き換える。
参照・被参照の関係が複雑になりやすく、メモリリークの原因になりうる。初めはハードコーディングで進め、必要になった段階でのパターン適用を奨める、とのこと。

■Adapterによるインタフェースの統合
Adapterパターンの適用の必要条件として3つ挙げられている。「2つのクラスの機能が同じか、似ている」、「それらのクラスがインタフェースを共有することでClientのコードの可読性が高まる」、「片方のクラスのインタフェースをプログラマが変更できない」、以上の3つ。条件を満たさない場合は、当然ながらコード設計が必要以上に複雑になる。
Clientにとって「好ましいインタフェース」の抽出からリファクタリングを開始し、変更できないインタフェースを好ましいインタフェースへ適応させること(Adapterクラスの設計)が作業の中心となる。

■Adapterの抽出
後方互換性などを目的に、状況によっては全く使わないようなメソッドがAdapter(ラッパ)クラスに含まれているような場合に行う。
基本的なメソッドを定義した抽象Adapterを抽出し、バージョンごとに対応するAdapterサブクラスを用意する。クラス内で複数のバージョン向けコードが混在することを防ぐことが出来る。
Facadeパターンとの違いは、Adapterがクラス単位のバージョンに対象とするのに対し、Facadeはシステム全体を対象とする窓口になる。つまり、ラップされたシステムを丸ごと置き換える場合などにFacadeが有効になる。

■Interpreterによる暗黙的な言語処理の置き換え
「暗黙的言語処理」って? データベースの検索などを行う場合に、出来るだけ背後にある文法などを意識しなくて済むように定義されたメソッドなどを指す、ってことでいいのかな?
かような「暗黙的言語処理」は単純なぶん柔軟性に欠けるため、より豊富な組み合わせのサポートや新しい言語要素の追加、動的な処理などを目的として行う。構文木をそれぞれCompositeオブジェクトに見立て、本来の文法に従って目的の言語処理に対応したインスタンス生成を行うように書き換える。
(なんかInterpreterってトークナイザやらパーサやらが出てくるイメージだけど・・・)

■クラスによるタイプコードの置き換え
タイプコードってのは定数フィールドですな。多くの場合整数型か文字列型が使用されている。この場合定数が代入される事が期待されているフィールドに、意図しない値が代入されてしまう恐れがある。しかも、意図しない代入の原因が文字列のタイプミスであった場合エラーが検出されにくい。
この場合、定数フィールドに専用のクラスを定義することで、無効な値を代入をエラーとして検出できるようになる。また列挙型を使用した場合と違って、各定数に対応した処理の追加などが容易になる。

■Singletonによるインスタンス化の制限
システムの性能の向上が求められており、かつオブジェクトが共有可能なものである場合には、Singletonを導入する価値はある。しかし性能改善に関わらないような場合は、インスタンス化の制限よりも「Singletonのインライン化」をまず検討すべき。

■ヌルオブジェクトの導入
あるクラス型オブジェクト変数について、ヌルかどうかによる条件ロジックがコードに散見される場合に、この条件分岐をすべて削除する方法。
クラスの各メソッドについてヌル時に実行するロジックでオーバライドしたサブクラスを作成し、このサブクラスのインスタンスをヌルの代わりに代入して使う(「ヌルオブジェクトパターン」あるいは「Active Nothing」)。
チェックを忘れたためにヌル参照エラーが発生する事を防止できるが、ヌルオブジェクトはスーパクラスのメソッドを全てオーバライドする必要があるため、保守がやや煩雑になる。

■Collecting Parameterによる累積処理の書き換え
メソッド内でローカル変数に情報を累積して結果として返す巨大な「累積メソッド」からメソッドの抽出を行うリファクタリング。その際同時に抽出されたローカル変数(必要に応じ結果の累積が可能な型に変更)がCollecting Parameterにあたる。
Compositeパターンと組み合わせると、再帰的な累積処理がよりシンプルに書き換えられる。
情報が累積されるタイミングで新しいインスタンスが生成されないよう注意。

■Visitorによる累積処理の書き換え
(Visitorパターン自体よく分かってないので、やっぱり軽く読んだだけでは分からなかった。)

■コンストラクタの連鎖
コードの重複を減らすため、可能な限りコンストラクタ内ではよりパラメータの多い別のコンストラクタを呼び出す。コンストラクタが増えることを厭う場合にはCreation Methodへの置き換えを検討する。

■インタフェースの統合
スーパクラスにサブクラスと同じインタフェースを持たせたい場合の手順の解説。

■パラメータの抽出
メソッドの引数をパラメータにしてフィールドのインスタンスを生成するコードについて、メソッドの引数としてインスタンス参照そのものを渡すインタフェースに書き換える手順の解説。
PR
この記事にコメントする
お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード   Vodafone絵文字 i-mode絵文字 Ezweb絵文字
この記事へのトラックバック
この記事にトラックバックする:
26歳のハローワールド
カレンダー
04 2024/05 06
S M T W T F S
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
最新コメント
[11/27 gyzwviyehl]
[11/18 Tepexaxyonelo]
[09/12 gomFolley]
[08/16 CypeBachCoece]
[06/02 gb]
[03/06 kishima]
[01/18 KNDY]
[01/16 kage]
[12/23 KNDY]
[12/23 kage]
最新トラックバック
ブログ内検索
アクセス解析
プロフィール
HN:
knd
HP:
自己紹介:
絶賛迷走中。
UNIQLO CALENDAR
忍者ブログ [PR]