BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル デザインパターンの自動化

デザインパターンの自動化

原文(投稿日:2013/03/05)へのリンク

はじめに

ソフトウェア開発プロジェクトは日に日に大きくなり、より複雑になってきている。プロジェクトがより複雑であれば、そのソフトウェアの開発と保守のコストがより高くなる傾向にあり、ハードウェアのコストをはるかに上回る。

ソフトウェアの規模とそれを開発し、維持するためのコストとの間には超線形関係がある。結局のところ、大規模で複雑なソフトウェアは、それを開発し、維持するために、優秀なエンジニアを必要とし、優れたエンジニアを得るのは難しく、留めるのは高価だ。

コード1行あたりの総所有コストが高いにもかかわらず、定型的なコードの多くは、まだ書かれており、その多くは、よりスマートなコンパイラを使って回避することがでる。実際、ほとんどの定型的なコードは、デザインパターンを繰り返し実装することに起因している。しかし、これらのデザインパターンのいくつかは、非常によく理解されており、もし我々がコンパイラにそれを教えることができれば、それらは自動的に実装することができる。

Observerパターンの実装

Observerパターンを例に取る。このデザインパターンは、1995年には早くも識別され、成功したModel-View-Controllerアーキテクチャのベースとなった。このパターンの要素は、Java(1995年、 Observable interface )とNET(2001年、INotifyPropertyChanged interface)の最初のバージョンでは実装されていた。インターフェイスは、フレームワークの一部だが、それらはまだ開発者が手動で実装する必要がある。

INotifyPropertyChanged interface は、単にPropertyChangedという名前の一つのイベントが持ち、オブジェクトのプロパティが別の値に設定されるたびに、それは通知される必要がある。

.NETで簡単な例を見てみましょう。

public Person : INotifyPropertyChanged
{

string firstName, lastName;
public event NotifyPropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if ( this.PropertyChanged != null ) {
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string FirstName
{
get { return this.firstName; }
set
{
this.firstName = value;
this.OnPropertyChanged(“FirstName”);
this.OnPropertyChanged(“FullName”);
}
public string LastName
{
get { return this.lastName; }
set {
this.lastName = value;
this.OnPropertyChanged(“LastName”);
this.OnPropertyChanged(“FullName”);
}
public string FullName { get { return string.Format( “{0} {1}“, this.firstName, this.lastName); }}}

プロパティは、最終的にフィールドのセットに依存しており、我々はそれに影響を与えるフィールドを変更すると、プロパティのPropertyChangedを起こさなければならない。

コンパイラーが我々のためにこの仕事を自動的にやってくれないだろうか?長い答えは、もし起こりえる全ての稀なケースを考慮に入れて、フィールドとプロパティ間の依存関係を検出することは、困難な作業である。プロパティが他のオブジェクトのフィールドに依存関係を持つことができ、それらが他のメソッドを呼び出す事ができ、あるいは更に悪い場合には、それらが仮想関数を呼び出したり、コンパイラーに unknownを委譲したり出来る場合がある。なので少なくともコンパイル時間が数秒から数分を期待し、数時間から数日を受け入れないなら、この問題に汎用的な解決は無い。しかし、実際の場合、プロパティはコンパイラーが完全に理解できるぐらい単純である。なので短い答えは、イェスで、コンパイラーは一般的なアプリケーションの全propertyの90%以上の通知コードを生成できる。

実際、同じクラスは以下のように実装できる。

[NotifyPropertyChanged]
public Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get { return string.Format( “{0} {1}“, this.FirstName, this.LastName); }}
}

このコードは、コンパイラーに何をすべき( INotifyPropertyChangedを実装する)を言っていて、どのようにそれをするかは言っていない。

定型的なコードは、アンチパターンだ

Observer(INotifyPropertyChanged)パターンは、通常、大規模なアプリケーションでは多くの定型的なコードを引き起こすパターンの一例にすぎない。しかし、典型的なソースベースがたくさんの決まり文句を生成するパターンでいっぱいです。それらは常に "公式"のデザインパターンとして認識されていない場合でも、コード・ベースの中で大量に繰り返しているので、それらはパターンだ。コードの繰り返しの中で最も共通な原因は次のとおり。

  • トレース、ロギング
  • 前提条件と不変条件のチェック
  • 認可および監査
  • ロックとスレッドのディスパッチ
  • キャッシング
  • (アンドゥ/リドゥ用の)変更のトラッキング
  • トランザクション処理
  • 例外処理

これらの機能は、通常のオブジェクト指向技術を使用してカプセル化することが困難であり、それゆえ、それらが、多くの場合、定型的なコードを使用して実装されている理由である。それがそんなに悪いことなのか?

そうだ。

定型的なコードを使用して、横断的な問題に対処するには、優れたソフトウェアエンジニアリングの基本原則の違反につながる

  • 単一責任の原則 に、違反しているのは、複数の関心事が、例えばバリデーション、セキュリティ、INotifyPropertyChanged、そしてUndo / Redoが単一のプロパティsetterによるように、同じメソッドで実装されている時である。
  • オープン/クローズド原則が言っているのは、ソフトウェアエンティティは拡張のためにオープンであるべきだが、修正に対してはクローズドであるべき、ということで、新しい機能が元のソースコードを変更せずに追加することができるときに最も重要視される。
  • DRY(重複しない)原則は、デザインパターンの手動実装から生まれるコードの繰り返しを嫌う。
  • 疎結合原則 が侵害されるのは、パターンを手動で実装する時である。なぜならこのパターンの実装を変更することが困難であるからだ。カップリングは、2つのコンポーネントの間だけでなく、コンポーネントと概念設計の間にも発生する可能性があることに注意して欲しい。ライブラリ同士が同じ概念設計を共有している場合は、ライブラリを別のライブラリを交換することは通常は簡単だが、異なるデザインを採用していると、ソースコードにより多くの変更を必要とする。

更に、定型的なコードは、以下のことを引き起こす。

  • 読むのと推論すのを難しくする。機能的要求を処理するためにコードが何をやっているのかを理解しょうとする時に。この追加された複雑さの層は、保守コストに大きく影響する。なぜならソフトウェア保守の75%の時間はコードを読むことに費やされるからである。
  • 大きくなる。この意味は、生産性を下げるばかりでなく、ソフトウェアの開発と保守のコストを高くする。バグを呼び込むリスクが高くなることは考慮していない。
  • リファクタリングと変更が難しくなる。(おそらくバグを修正)定型的なコードを変更すると、それらが適用されていたすべての場所を変更する必要がある。潜在的に多くのソリューションと/あるいはリポジトリにまたがっているコードベース全体で使用されている定型的なコードを正確に特定するにはどうすればよいのか?検索と置換...?

放置していると、定型的なコードは蔓のようにコードの周りで成長する厄介な習性があり、新しいコードにそれが適用される度にもっとスペースを喰い、最終的に大きなコードベースのほとんど全体が定形的コードで埋められることになる。私の以前のチームの1つでは、単純なデータアクセス層のクラスが様々なタイプのSQL例外とリトライを扱うのに、1000行以上になり、その内の90%が定形的コードだった。

これであなたがなぜ定形的コードを使うのがパターンを実装するのにひどい方法なのかを理解できた、と願いたい。実際、それは避けるべきアンチパターンであり、それは不要な複雑さ、バグ、高価な保守、生産性の損失と結局は、より高いソフトウェアコストに繋がる。

デザインパターンの自動化とコンパイラの拡張機能

非常に多くの場合、共通の定型的コードを再利用可能にする戦いは、C#やJavaのような主流の静的型づけされた言語において、ネイティブなメタ言語のサポートが無いことに起因している。

コンパイラーは普通我々の届かないところで、コードについて驚くほど多くの情報を持っている。もしこの情報から恩恵を得て、デザインパターンに役立つコンパイラー拡張機能を書くことができたら、素晴らしいことだろう。

もっと賢いコンパイラーは以下のことを可能にする。

  1. ビルド時のプログラム変換:コードのセマンティクスを保持し、チェックしているコードの複雑さと行数を維持しながらフィーチャを追加できる。従って自動化できるデザインパターンの部分を自動的に実装できる。
  2. 静的コード検証:ビルド時の安全性によって、デザインパターンを正しく使ったことを確認したり、あるいは自動化できないパターの部分が事前に定義されたルールのセットに従って実装されていることをチェックできる。

例:C#における‘using’と‘lock’ キーワード

もしデザインパターンが直接コンパイラーでサポートできるか証明したければ、usinglockキーワード以外を見る必要は無い。一見すると、それらは言語の中で純粋に冗長です。しかし言語の設計者は、それらの重要性を認識し、それらに固有のキーワードを作った。

usingキーワードを見てみよう。このキーワードはより大きな Disposable Patternの一部で、このパターンは、以下の構成要素からできている

  • リソースオブジェクトは、データベース接続など、外部リソースを消費するオブジェクト だ。
  • リソースコンシューマは、ある寿命の間、リソースオブジェクトを消費する命令ブロックあるいは、オブジェクトである。

Disposable Patternは以下の原則に支配されている。

  1. リソースオブジェクトは、IDisposableを実装しなければならない。
  2. IDisposable.Disposeの実装は冪等でなければならない、すなわち、安全に複数回呼び出させる。
  3. リソースオブジェクトはファイナライザを持つ必要がある(C++でデストラクタと呼ばれる)。
  4. IDisposable.Disposeの実装は GC.SuppressFinalizeを呼びださなければならない。
  5. 一般的に Resource Objectsを状態(フィールド)に保存するオブジェクトもResource Objectsであり、子供のResource Objectsはその親によって破棄されなければならない。
  6. Resource Objectsを割り当て、消費する命令ブロックは、usingキーワードで囲まなければならない(もしリソースへの参照がオブジェクトの状態に保存されていなければ。以前の要点を参照。)

見たとおり、 Disposable Patternは、一見の見た目よりも内容がある。このパターンをいかに自動化し、強制するか?

  • core .NETライブラリはIDisposableインターフェースを提供している。
  • C#コンパイラは、usingキーワードを提供し、それはあるソースコード(try/finallyブロック)を自動生成する。
  • FxCopは、いかなるディスポーザブルなクラスもファイナライザを実装し、Disposeメソッドは GC.SuppressFinalizeを呼び出す、というルールを強制できる。

従って、 Disposable Patternは.NETプラットフォームに直接サポートされたデザインパターンの完全な例である。

しかし、本質的にはサポートされていないパターンについてはどうか?それらは、クラスライブラリとコンパイラの拡張機能を組み合わせることで、実装できる。

例:コードコントラクト(Code Contracts)

前提条件(そしてオプションで事後条件と不変性)のチェックは長い間、別のコンポーネントでの症状を引き起こす、あるコンポーネントにおける欠陥を防止するベストプラクティスとして認識されてきた。

  • あらゆるコンポーネント(あらゆるクラス、典型的には)「セル」として設計されるべきである。
  • あらゆるセルは、自分自身の健康に責任を持つ。従って、
  • 各セルは、他のセルから受け取る自分へのあらゆる入力をチェクすべきである。

前提条件チェックは、繰り返し発生する問題に再現性のある解決策であるため、デザインパターンを考えることができる。

Microsoft Code Contracts (http://msdn.microsoft.com/en-us/devlabs/dd491992.aspx) は、デザインパターンの自動化の完璧な例である。プレーンで古い C# やVisual Basicをベースにして、それは事前条件、事後条件、およびオブジェクト不変性の形式で検証ルールを表現するためのAPIを提供する。しかし、このAPIは、単なるクラスライブラリではない。それはプログラムをビルド時の変換と検証に翻訳する。

コードコントラクトに関して余りに詳細に掘り下げるのは止める。簡単にいえば、それは、実行時と同様にビルド時にチェックできるようにコード中に検証ルールを指定できるようにしてくれる。例えば、

public Book GetBookById(Guid id)
{
Contract.Requires(id != Guid.Empty);
return Dal.Get<Book>(id);
}

public Author GetAuthorById(Guid id)
{
Contract.Requires(id != Guid.Empty);

return Dal.Get<Author>(id);
}

そのバイナリ・リライターは(あなたの構成に基づいて)あなたのビルド済アセンブリを書き換え、追加コードを注入でき、あなたが指定した様々な条件を検証する。もしバイナリ・リライターによって生成された変換コードをよく見ると、以下のように行に追加されているものがある。

  public Book GetBookById(Guid id)
{
if (__ContractsRuntime.insideContractEvaluation <= 4)
{
try
{
++__ContractsRuntime.insideContractEvaluation;
__ContractsRuntime.Requires(id != Guid.Empty, (string)null, "id !=
Guid.Empty"
);
}
finally
{
--__ContractsRuntime.insideContractEvaluation;
}
}
return Dal.Get<Program.Book>(id);
}
public Author GetAuthorById(Guid id)<
{
if (__ContractsRuntime.insideContractEvaluation <= 4)
{
try
{
++__ContractsRuntime.insideContractEvaluation;
__ContractsRuntime.Requires(id != Guid.Empty, (string)null, "id !=
Guid.Empty"
);
}
finally
{
--__ContractsRuntime.insideContractEvaluation;
}
}
return Dal.Get<Program.Author>(id);
}

Microsoftのコードコントラクトの詳細については、ここにJon Skeet氏の優れたInfoQの記事を読んで欲しい。(http://www.infoq.com/articles/code-contracts-csharp).

コードコントラクトのようなコンパイラ拡張は素晴らしいが、公式のサポートされる拡張機能は、開発、成熟、安定するのに普通数年かかる。余りに多くの異なるドメインがあり、それぞれにはそれ自身の問題セットがあり、公式の拡張機能でそれら全てをカバーすることは不可能である。

我々が必要としているのは、汎用のフレムワークで、規律のある方法でデザインパターンの自動化と強制を助けるものである。そうなれば我々は、ドメイン特有の問題を我々自身で効果的に処理できる。

デザインパターンを自動化し、強制する汎用フレームワーク

解決策として、動的言語、オープンコンパイラ(例えばRoslyn)、または再コンパイラ(例えばCecil)に求めたくなるかもしれ ない、なぜならそれらは、抽象構文木の詳細を公開するからである。しかし、これらの技術は、抽象化の過剰なレベルで動作し、あらゆる変換、最も単純なものでも実装するのは、大変複雑になる。

我々に必要なのは、以下の原則に基づいた、コンパイラーの拡張用の高レベルなフレームワークである。

1. 変換プリミティブのセットを提供する。例えば、

  • メソッド呼び出しの横取り
  • メソッド実行前後におけるコードの実行
  • フィールド、プロパティ、イベントへのアクセスの横取り
  • 既存クラスにインターフェース、メソッド、プロパティ、イベントの追加

2. どこにプリミティブを適用するかを表現する方法を提供する。コンパイラーの拡張機能にあるメソッドを横取りしたい、言うのはいいが、どのメソッドを横取りするのか知っていればもっと良いだろう。

3. プリミティブは安全に組立可能でなければならない

自分のコードの同じ場所(複数可)に複数の変換を適用できるようにしたいのは当然なので、フレームワークは、変換を組み立てる能力を与えるべきである。

複数の変換を同時に適用できるときに、いくつかの変換は、他との関係で特定の順序で発生する必要があるかもしれない。したがって、変換の順序は明確に定義された規則に従っているが、それでも適切なら、デフォルトの順序を上書きできるようにする必要がある。

4. 強化されたコードのセマンティクスが影響を受けるべきではない

変換メカニズムは、目立たなくすべきで、できるだけ元のコードが変わらないようにすべきで、同時に静的に変換を検証する機能を提供すべきである。フレームワークは、余りに簡単にソースコードの意図を「破る」べきでない。

5. 高度なリフレクションと検証機能

定義によって、デザインパターンは、いかにそれが実装されるべき定義したルールを持っている。例えばロッキングデザインパターンは、インスタンスフィールドは、同じオブジェクトのインスタンスメソッドからのみアクセスできる、と定義できる。フレームワークは、あるフィールドにアクセスするクエリメソッドのメカニズムとクリーンなビルド時エラーを発生する方法を提供しなければならない。

アスペクト指向プログラミング

アスペクト指向プログラミング(AOP)は、関心事の分離を可能にすることで、モジュール性を高めることを目指したプログラミングパラダイムである。

An アスペクトはコード変換(アドバイスと呼ばれる)、コードマッチングルール(ポイントカットと原始的に呼ばれる)、コード検証ルールとを持つ特殊な種類のクラスである。デザインパターンは、典型的に1つないし幾つかのアスペクトで実装される。コードにアスペクトを適用する方法は幾つかあり、それは各AOPフレームワークにかなり依存している。カスタムアトリビュート(Javaのアノテーション)は、便利な方法でアスペクトをコードの厳選された要素に追加できる。もっと複雑なポイントカットは、XML(例えば Microsoft Policy Injection Application Block)やドメイン固有言語(例えばAspectJ や Spring)を使って宣言的に表すことができたり、あるいはリフレクション(例えば PostSharpで LINQ over System.Reflection)を使ってプログラム的に表現できる。

ウィービングプロセスは、指定した場所(同様に原始的にジョイントポイントと呼ばれる)で元のソースコードとアドバイスを組み合わせる。元のソースコードについてメタデータにアクセスできるので、C#やJavaのようなコンパイルされた言語において、静的なウィーバーが静的解析を実行して、ウィービングプロセスが適用された、ポイントカットに関連したアドバイスの有効性を確認できる可能性がある。

アスペクト指向プログラミングとデザインパターンが独立して概念化されてきたが、AOPはたり、デザインルールを強制しようとする人々には、優れたソリューションだ。低レベルのメタプログラミングとは異なり、AOPは、上記のように引用した原則に基づいて設計されているので、コンパイラの専門家だけでなく、誰でもデザイン・パターンを実装することができる。

AOPは、プログラミングパラダイムであり技術ではない。そうなので、それは様々なアプローチを使用して実装できる。AspectJは、Java用の最先端のAOPフレームワークだが、今やEclipseのJavaコンパイラに直接実装されている。コンパイラがオープンソースではない.NETでは、AOPは、C#またはVisual Basicコンパイラの出力を変換する、最高のリコンパイラとして実装されている。.NETにおける最先端のツールはPostSharp(下記参照)だ。代わりに、AOPの限られたサブセットは、動的プロキシとサービスコンテナを使用して達成され、ほとんどの依存性注入フレームワークは、少なくともメソッド横取りアスペクトを提供することができる。

例:PostSharpによるカスタムデザインパターン

PostSharpは、Microsoft .NETにおけるデザインパターンの自動化と強制のための開発ツールであり、.NET用の最も完全なAOPフレームワークを提供してい る。

この記事が PostSharpのチュートリアルにならないように、大変簡単なパターンを取り上げよう。フォアグラウンド(UI)スレッドとバックグラウンドスレッド間でメソッドの実行のディスパッチし合っている。このパターンは2つの簡単なアスペクトを使って実装できる。1つは、バックグラウンドスレッドにメソッドをディスパッチする。別のはそれをフォアグラウンドにディスパッチする。両アスペクトは、無料の PostSharp Expressでコンパイルできる。最初のアスペクト、BackgroundThreadAttributeを見てみよう。

パターンの生成部分は単純だ。我々は、そのメソッドを実行するタスクを生成し、そのタスクの実行をスケジュールする必要があるだけだ。

[Serializable] 
public sealed class BackgroundThreadAttribute : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
{
Task.Run( args.Proceed );
}
}

MethodInterceptionArgsのクラスには、引数および戻り値のようなメソッドが呼び出されるコンテキストに関する情報が含まれている。この情報によって、元のメソッドを呼び出し、その戻り値をキャッシュし、その入力引数を記録したり、あなたのユースケースに必要なことを何でもできるようになる。

パターンの検証の部分については、我々は、戻り値または参照によって渡されるパラメータを持つメソッドにカスタム属性を適用するのは回避したいと思う。この問題が発生した場合、我々は、ビルド時のエラーを生成したい。したがって、我々はCompileTimeValidateメソッドをBackgroundThreadAttributeクラスに実装しなければならない。

// Check that the method returns 'void', has no out/ref argument.
public override bool CompileTimeValidate( MethodBase method ) {

MethodInfo methodInfo = (MethodInfo) method;

if ( methodInfo.ReturnType != typeof(void) ||
methodInfo.GetParameters().Any( p => p.ParameterType.IsByRef ) )
{
ThreadingMessageSource.Instance.Write( method, SeverityType.Error, "THR006",
method.DeclaringType.Name, method.Name );
return false;
}

return true;
}

ForegoundThreadAttributeは似たようになり、WPFで Dispatcherオブジェクトあるいは、WinFormsで BeginInvokeメソッドを使う。

上記のアスペクトは、他のどのような属性のように適用出来る。例えば、

[BackgroundThread]
private static void ReadFile(string fileName)
{
DisplayText( File.ReadAll(fileName) );
}
[ForegroundThread] private void DisplayText( string content ) { this.textBox.Text = content;
}

生成されたソースコードは、我々は直接タスクおよびディスパッチャを使用することによって得られるものよりもはるかにきれいだ。

C# 5.0はasyncawaitキーワードを使ってこの課題をもっと上手く解決している、と主張する人がいるかもしれない。それは正しいし、またC#チームが何度も現れる問題を認識し、直接コンパイラーとコアクラスライブラリに実装したデザインパターンで解決することを決めた、良い例である。.NET開発コミュニテイは、このソリューションを2012年まで待たなければならなかったが、 PostSharpは早くも2006年にソリューションを提供していた。

あと何年.NETコミュニテイは、他の共通のデザインパターン、例えば INotifyPropertyChangedに対するソリューションを待たなければならないのか?そしてあなたの会社のアプリケーションフレームワークに固有のデザインパターンについては、どうだろう?

より賢いコンパイラーは、あなた自身のデザインパターンを実装できるので、あなたのチームの生産性を改善するためにコンパイラーベンダーに頼る必要はない。

AOPの欠点

ここまでで、あなたがAOPはデザインパターンを自動化し、良いデザインを強制する現実的なソリューションであると確信していることを望むが、幾つかの欠点もあることを覚えておく価値がある。

1. スタッフの準備不足

パラダイムとして、AOPは学部で教えておらず、マスター・レベルでも触れられることは殆ど無い。教育の欠如は、開発者コミュニティの間でのAOPについての一般的な認識の欠如に貢献してきた。

20歳であるにもかかわらず、AOPは、しばしば最も冒険的な開発チームだけが採用する障害物であることがわかった「新しい」パラダイムとして誤解されている。

デザインパターンもほぼ同じ年齢だが、デザインパターンが自動化でき、検証できる、という考えは最近である。我々は、この記事で幾つか意味のある前例を引用してきた、その中にはC#コンパイラー、.NETクラスライブラリ、Visual Studio Code Analysis (FxCop)を含んだが、これらの前例はデザインパターンの自動化のための一般的な呼び出しに、一般化されていない。

2. 驚き要因

スタッフと学生は同じように準備できていないので、彼らがAOPに出くわしたら驚きの要素となり得る。なぜならアプリケーションには、ソースコードから直接見えない追加の動作があるからである。注:驚くのは、AOPの意図した高価であり、コンパイラーが普通以上のことをして、副作用が無いことである。

意図しない効果による驚きもあり得る。それは、アスペクト(すなわちポイントカットで)の使い方のバグが変換を期待していないクラスやメソッドに適用される原因になる時だ。そのようなエラーのデバッグは、微妙であり、もし開発者がプロジェクトにアスペクトが適用されていることを知らない場合には特にそうである。

These surprise factors can be addressed by:

  • IDEの統合が以下の可視化を助ける。(a)どの追加機能がソースに適応されているかをエディタに表示する(b)コードのどの要素にあるアスペクトが適用されているか。これを書いている時点では、わずか2つのAOPフレームワークが正しいIDE統合を提供している。AspectJ (Eclipse用のAJDTプラグインによって)とPostSharp (Visual Studio用)である。
  • 開発者による単体テスト-アスペクト、そしてアスペクトが適切に適用されているという事実は、あらゆる他のソースコード成果物のように単体テストされなければならない。
  • アスペクトをコードに適用する際、命名の慣例に頼るのではなく、型継承やカスタム属性のようなコードの構造的プロパティに依存している。この議論はAOPに限ったことではないことに注意する。慣例ベースのプログラミングは、最近勢いを得ている。しかしそれはまた、驚きになりやすい。

3. 政治

デザインパターンの自動化の使用は、一般的に政治的に敏感な問題である 。なぜならそれは、チーム内で関心の分離を対処するからである。典型的には、上級開発者は、デザインパターンを選択し、アスペクトを実装する。そしてジュニア開発者は、それらを使用する。上級開発者が検証ルールを記述し、手書きのコードがアーキテクチャを尊重することを確実にする。ジュニアの開発者が全体のコードベースを理解する必要はないという事実は、実際には意図された効果である。

この議論は、シニアマネージャーの視点を取り、ジュニアの開発者のプライドを傷つける可能性があるので、一般的に取り組むにはデリケートものである。

PostSharpのパターンライブラリを使用した既製のデザインパターンの実装

Disposableパターンで見たように、一見シンプルなデザインのパターンは実際には複雑なコード変換や検証を必要とすることがある。これらの変換および検証の一部は複雑だが、まだ自動的に実装するのは可能である。その他は自動処理にはあまりにも複雑になりえるので、手動で行う必要がある。

幸いなことに、AOPフレームワークを使って誰でもで簡単に自動化できるシンプルなデザインパターンもある(例外処理、トランザクション処理、およびセキュリティ)。

長年の市場経験の後に、 PostSharpチームは、殆どの顧客が同じアスペクトを繰り返し実装していることを認識して、最も共通のデザインパターンの高度に洗練し、最適化した既製の実装を提供し始めた。

PostSharpは現在、次のデザインパターン用の既製の実装を提供している。

  • マルチスレッド:リーダ・ライタ同期スレッドモデル、アクタースレッドモデル、スレッド-排他的スレッドモデル、スレッドディスパッチ
  • 診断:高パフォーマンスで、NLog とLog4Netを含む様々なバックエンドへの詳細なロギング
  • INotifyPropertyChanged:複合プロパティと他のオブジェクトへの依存関係のサポートを含む
  • コントラクト:パラメータ、フィールド、およびプロパティの検証。

これで、デザインパターンの既製の実装を使って、チームはAOPを学習せずに、AOPの恩恵に浴することができる。

要約

JavaやC#などのいわゆる高レベル言語は、なお開発者に抽象化の無関係なレベルでコードを書くことを強いる。主流のコンパイラー限界のために、開発者はたくさんの定型的コードを書くことを強いられている。それらがアプリケーションの開発と保守するコストを増している。定型的コードは、手動によるパターンの大量実装に起因していて、それが業界におけるコピー&ペースト継承の最大用途かもしれない。

デザインパターンの実装を自動化することができないことは多分、ソフトウェア業界に何十億の費用 負担となっている。この中には、ビジネス価値を追加するのではなく、インフラの問題に時間を費やしている質の高いソフトウェアエンジニアの機会コストを考慮していない。

しかし、もし我々がもっと賢いコンパイラーを持ち、最も共通のパターンの実装を自動化できれば、大量の定型的コードを除くことができる。願わくば、将来の言語設計者は、デザインパターンが現在のアプリケーション開発の一級市民であることを理解し、コンパイラーに適切なサポートを持つべきである。

しかし、実際には、新しいコンパイラを待つ必要は無い。それらは既に存在しており、成熟している。アスペクト指向プログラミングは、特に定型的なコードの問題に対処するために設計された。AspectJとPostSharpの両方共、これらの概念の成熟した実装であり、世界中の最大規模の企業で使用されている。PostSharpとSpring Rooの両方が最も共通のパターンの既製の実装を提供している。いつものように大衆が続く数年前に、早期導入者は生産性の向上を得ることができる。

Gang of Four の新時代を画する本が出てから18年が過ぎた、デザインパターンが大人になる時ではないだろうか?

著者について

Gael Fraiteur氏は、子供の頃から情熱的にプログラミングをしてきた。彼は、12歳の時に最初の有償ソフトウェアを作り、売った。彼はチェコ共和国のプラハを本拠にする PostSharp Technologiesの創立者で、主席エンジニアである。彼は、アスペクト指向プログラミングの専門家として広く知られており、ヨーロッパと米国の開発者カンファレンスで講演している。

 

 

Yan Cui氏は、ロンドンを本拠とする、GameSysのソーシャルゲーム部門iwiで働くC#/F#開発者で、Facebook や Hi5のようなプラットフォームで動くソーシャルゲーム用の高度に分散し、スケーラブルなサーバー側ソリューションの構築にフォーカスしている。彼は、イギリスの地元ユーザーグループやカンファレンスでC#やF#に関して定期的に講演している。また活発にブログをメンテしている。

 

この記事に星をつける

おすすめ度
スタイル

BT