プログラミングパラダイム
IT開発の現代世界では、プログラムを作成するための非常に多くの異なるアプローチがあります。 そのため、たとえば、誰かがプログラムを一連のアクションの形式で提示するのを好む一方で、誰かがプログラムは互いに通信するオブジェクトのセットであると考えています。 これらのアイデアと概念の組み合わせは、プログラムの一種の執筆スタイルを形成し
ます 。これは一般
にプログラミングパラダイムと呼ばれ
ます 。
各パラダイムには独自の特性がありますが、それらを区別する主な要因は、プログラムの基本単位の概念です。 最も人気のあるものは次のとおりです。
- 命令(命令型プログラミング、FORTRAN / C / PHP)、
- 関数(関数型プログラミング、Haskell / Lisp / F#/ Scala)、
- プロトタイプ(プロトタイププログラミング、JavaScript)、
- オブジェクト(オブジェクト指向プログラミング、C ++ /ジャワ)、
- 事実(論理プログラミング、PROLOG)。
一般的な場合、プログラミング言語は使用されるパラダイムを一意に決定しないことに注意してください。同じPHPで、命令型プログラムとオブジェクト指向プログラムの両方を作成できます。
この記事では、比較的若いが、非常に有用なプログラミングパラダイムである
アスペクト指向プログラミングについて説明します。
AOPの基本
次のメソッドを実装する真空内の球状のサービス(Webサービスなど)を検討してください。
public BookDTO getBook(Integer bookId) {
BookDTO book = bookDAO.readBook(bookId);
return book;
}
この方法は非常にシンプルで明白です。本の情報を本の識別子で読むことです。 しかし、ここで何が欠けているのか考えてみましょう。 まず、ロギングについて考える必要があります。ロギングがないと、ご存じのとおり、Webサービスではどこにも行けません。
public BookDTO getBook(Integer bookId) {
LOG.debug( "Call method getBook with id " + bookId);
BookDTO book = bookDAO.readBook(bookId);
LOG.debug( "Book info is: " + book.toString());
return book;
}
次に、例外処理を実装する必要があります(サービス層に対応する例外を返させ、基礎となる層の例外を非表示にするため)。
public BookDTO getBook(Integer bookId) throws ServiceException {
LOG.debug( "Call method getBook with id " + bookId);
BookDTO book = null ;
try {
book = bookDAO.readBook(bookId);
} catch(SQLException e) {
throw new ServiceException(e);
}
LOG.debug( "Book info is: " + book.toString());
return book;
}
また、アクセス権の確認も忘れないでください。
public BookDTO getBook(Integer bookId) throws ServiceException, AuthException {
if (!SecurityContext.getUser().hasRight("GetBook"))
throw new AuthException("Permission Denied");
LOG.debug( "Call method getBook with id " + bookId);
BookDTO book = null ;
try {
book = bookDAO.readBook(bookId);
} catch (SQLException e) {
throw new ServiceException(e);
}
LOG.debug( "Book info is: " + book.toString());
return book;
}
さらに、作業結果をキャッシュすることは理にかなっています。
public BookDTO getBook(Integer bookId) throws ServiceException, AuthException {
if (!SecurityContext.getUser().hasRight( "GetBook" ))
throw new AuthException( "Permission Denied" );
LOG.debug( "Call method getBook with id " + bookId);
BookDTO book = null ;
String cacheKey = "getBook:" + bookId;
try {
if (cache.contains(cacheKey)) {
book = (BookDTO) cache.get(cacheKey);
} else {
book = bookDAO.readBook(bookId);
cache.put(cacheKey, book);
}
} catch (SQLException e) {
throw new ServiceException(e);
}
LOG.debug( "Book info is: " + book.toString());
return book;
}
この方法は引き続き改善できますが、最初は十分です。 改善の過程で、元のサイズを超えるメソッドを10回(2〜20 LOC)受け取りました。 最も興味深いのは、その中のビジネスロジックのボリュームが変更されていないことです-それはまだ同じ1行です。 残りのコードは、アプリケーションのいくつかの一般的なユーティリティ機能を実装します:ロギング、エラー処理、アクセス制御、キャッシュなど。
原則として、ビジネスロジックをサービス機能と組み合わせることは、アプリケーションが小さい限りそれほど怖いものではありません。 ただし、プログラムが複雑になるほど、一般的にそのアーキテクチャに注意を払い、特に全体的な機能を強調する必要があります。 そのため、プログラミング言語の進化を観察すると、まず関数、次にモジュール、そしてオブジェクトの外観が見られます。 ただし、いくつかの一般的な機能を強調するには、上記のパラダイムでは不十分であることが実践で示されています。 そのような機能は「パススルー」または「分散」と呼ばれます。これは、その実装が実際にアプリケーションのさまざまな部分に分散しているためです。 上記のクロスカット機能の例は次のとおりです。
アスペクト指向プログラミング(AOP)の主な目的は、エンドツーエンド機能のモジュール化、アスペクトへの割り当てです。 これを行うために、AOPの概念をサポートする言語は、次のツールを実装して、エンドツーエンドの機能を強調しています。
- アスペクト-エンドツーエンド機能を実装するモジュールまたはクラス。 アスペクトは、スライスによって定義された結合ポイントにアドバイスを適用することにより、残りのコードの動作を変更します。 アスペクトを使用して機能を実装することもできます。
- アドバイス-追加ロジック-接続ポイントから呼び出す必要のあるコード。 ヒントは、結合ポイントの前、後、または代わりに実行できます。
- 結合点(結合点)-アドバイスが適用される、実行中のプログラム内の点(メソッド呼び出し、オブジェクト作成、変数アクセス)。
- スライス(ポイントカット)-接続ポイントのセット。 スライスは、特定の接続ポイントが特定のチップに適合するかどうかを決定します。
- 導入-クラス構造の変更および/または継承階層の変更による、外部コードへのアスペクト機能の追加。
- target-ヒントが適用されるオブジェクト。
- ウィービング-オブジェクトを関連する側面とリンクします(コンパイル、ロード、またはプログラム実行の段階で)。
使用例(AspectJの)
AspectJは、Java言語のアスペクト指向の拡張機能/フレームワークです。 現時点では、これはおそらく最も人気があり開発中のAOPエンジンです。
その助けを借りて、ロギングの側面の実装を検討してください。
@Aspect
public class WebServiceLogger {
private final static Logger LOG =
Logger.getLogger(WebServiceLogger. class );
@Pointcut( "execution(* example.WebService.*(..))" )
public void webServiceMethod() { }
@Pointcut( "@annotation(example.Loggable)" )
public void loggableMethod() { }
@Around( "webServiceMethod() && loggableMethod()" )
public Object logWebServiceCall(ProceedingJoinPoint thisJoinPoint) {
String methodName = thisJoinPoint.getSignature().getName();
Object[] methodArgs = thisJoinPoint.getArgs();
LOG.debug( "Call method " + methodName + " with args " + methodArgs);
Object result = thisJoinPoint.proceed();
LOG.debug( "Method " + methodName + " returns " + result);
return result;
}
}
最初のステップは、サービスメソッドのロギングの
アスペクト 、つまり
AspectアノテーションでマークされたWebServiceLoggerクラスを作成することです。 次に、接合点の2つのスライスが定義されます:webServiceMethod(WebServiceクラスに属するメソッドを呼び出す)とloggableMethod(@Loggableアノテーションでマークされたメソッドを呼び出す)。 最後に、ヒントが宣言され(logWebServiceCallメソッド)、スライスを満たす接続ポイント(
Aroundアノテーション)の代わりに実行されます( "webServiceMethod()&& loggableMethod()")。
評議会コードでは、現在のメソッド(接続ポイント)、メソッドの開始のログ、要求されたメソッドの直接呼び出し、作業の結果のログと返送に関する情報を受け取ります。
AspectJには、サポートされている接続ポイントのスライスがかなり大量にあります。 主なものは次のとおりです。
- execution(static * com.xyz .. *。*(..))-com.xyzパッケージ内の任意の静的メソッドのコードの実行。
- call(void MyInterface。*(..))-MyInterfaceインターフェースのvoidを返すメソッドの呼び出し。
- 初期化(MyClass || MyOtherClass)-MyClassまたはMyOtherClassクラスの初期化。
- staticinitialization(MyClass + &&!MyClass)-MyClassではなくMyClassで始まる名前のクラスの静的初期化。
- handler(ArrayOutOfBoundsException)-例外ハンドラーの実行ArrayOutOfBoundsException;
- get / set(static int MyClass.x)-クラスMyClassのプロパティxの読み取り/書き込み。
- this / target(MyClass)-タイプMyClassのオブジェクトに対応する接続ポイントの実行。
- args(整数)-整数型の引数が利用可能な接続ポイントの実行。
- if(thisJoinPoint.getKind()。equals( "call"))-指定された式が真であるすべての接続ポイントに一致します。
- within / withincode(MyClass)-指定されたクラスのコードで見つかったすべての接続ポイントに一致します。
- cflow / cflowbelow(call(void MyClass.test()))-指定されたスライスの実行スレッドで検出されたすべての接続ポイントに一致します。
- @annotation(MyAnnotation)-ターゲットが@MyAnnotationアノテーションでマークされている結合ポイントを実行します。
ヒントについては、その数ははるかに少ないですが、必要な多くの状況をすべて網羅しています。
- 前 - 接続ポイントにボードの立ち上げ、
- 戻った後-接続ポイントの通常の実行後にボードを起動し、
- スロー後-結合ポイントの実行中に例外をスローした後にアドバイスを開始します。
- after-接続ポイントの実行後にボードを起動します。
- around-ジャンクションを実行する代わりにカウンシルを起動します(ジャンクションの実行は、カウンシル内で呼び出すことができます)。
AspectJデザインの詳細については、
公式ドキュメントの対応するセクション[
1、2 ]を参照してください。
AspectJアスペクトを使用するには、特別な
AJCコンパイラを使用してコンパイルし、メインクラスに
縫い付ける必要があります。
製品は無料です。 Eclipseライセンスの下で配布されます。
使用例(PostSharp)
PostSharpは、.NETプラットフォーム用のアスペクト指向フレームワークです。 .NETには他にもAOPの実装がありますが、PostSharpサイトからの
比較から判断する
と 、主導的地位を保持しているのは彼です。
例外処理の側面を説明するための使用方法を検討してください。 最初のステップは、関連するアスペクトを拡張するクラスを作成することです:
public class ExceptionDialogAttribute : OnExceptionAspect
{
public override void OnException(MethodExecutionEventArgs eventArgs)
{
string message = eventArgs.Exception.Message;
Window window = Window.GetWindow((DependencyObject)eventArgs.Instance);
MessageBox.Show(window, message, "Exception" );
eventArgs.FlowBehavior = FlowBehavior.Continue;
}
}
厳密に言えば、PostSharpの用語の側面は、見てわかるように、AOPの用語の側面とアドバイスです。
このアスペクトの交差点のスライスを指定するには、アセンブリ設定ファイル(AssemblyInfo.cs)に次の行を追加します。
[assembly: ExceptionDialog ( AttributeTargetTypes= "Example.WorkflowService.*" ,
AttributeTargetMemberAttributes = AttributeTargetElements.Public )]
または、ExceptionDialog属性を使用して、目的のメソッドを明示的にマークします。
[ExceptionDialog]
public BookDTO GetBook(Integer bookId)
それだけです。対応するメソッドでスローされるすべての例外は、作成されたアスペクトによって処理されます。
PostSharpがアドバイスとアスペクトの概念を部分的に接着しているという事実のため、後者にはかなりの数があります。 詳細は
ドキュメントに記載されてい
ます 。 主なものは次のとおりです。
- OnMethodBoundary / OnMethodInvocation-メソッドの呼び出し(開始、終了、終了、例外付きで終了);
- OnFieldAccess-プロパティへのアクセス。
- onExceptionを - 例外処理。
- 構成-コードインジェクション。
PostSharpを機能させるには、プロジェクトに接続する必要のあるコンパイラとライブラリが必要です。 ステッチングの側面は、アプリケーションのビルド中に後処理バイトコードに基づいています。
製品は支払われます。 コミュニティ版があります。
理論から実践へ
それで、アプリケーションのエンドツーエンド機能の「括弧から抜け出す」という問題をどれほど美しく効果的に解決できるかを見てきました。 ただし、これはすべて理論です。 実際には、もちろん、すべてが少し異なります:)
まず、どちらの場合も、アスペクトのコンパイルと「ウィービング」のために、特別なコンパイラを使用し、プロジェクトとともに追加のライブラリをドラッグする必要があります。 これは問題ではないようです:コンパイラは簡単にダウンロードして環境に統合できます(たとえば、mavenを使用する場合、タスクはアスペクトj-maven-pluginプラグインの追加に削減されます)、少なくともJavaアプリケーションでは多くの依存関係が一般的です(同じMavenを使用して解決)。 ただし、プロジェクトに個別のコンパイルが必要なものを含める必要があり、潜在的な利点にもかかわらずすべての開発者を怖がらせることがあります。
この場合、Spring Framework [
1、2 ]が問題の解決策になる可能性があります。 このフレームワークには多くの利点がありますが、この記事のフレームワーク内では、AOPコンポーネントに関心があります。 Spring Frameworkは、プロキシオブジェクト(JDK Dynamic Proxy、CGLIB)を作成することにより、サードパーティライブラリを使用せずに、Pure Java(C#)で制限された
AOP機能を実装します。 つまり、Spring AOPでは、「メソッド実行」タイプの結合ポイントのみを使用できます。 ただし、実践が示すように、ほとんどの問題を解決するにはこのタイプの接続ポイントが必要なので、この制限は重要な役割を果たしません。
さらに、Spring Frameworkは、@ AspectJアノテーションを使用したアプリケーションの構成、およびAspectJを使用して直接コンパイルされたアスペクトの統合をサポートします。
当社では、Spring AOPを使用しています。 私の意見では、Spring Frameworkのその他のメリットを考慮すると、AOPを使用するのに最もアクセスしやすく便利なプラットフォームであり、その普及と開発に大きく貢献しています。
まとめ
要約すると、AOPに関する3つの主な考えを強調したいと思います。
- AOPの主な目的は、「一般的な」(エンドツーエンド)機能を「括弧から外す」こと(エンドツーエンド機能のモジュール化)を取り除くことです。
- Javaの場合、AOPはAspectJプロジェクトから、.NETの場合はPostSharpから入手できます。
- AOPの最も単純で実績のある実装は、Spring AOPです。
関連リンク