狠狠撸

狠狠撸Share a Scribd company logo
LaravelAspectで関心の分離
KenjiroKubota
2017/07/08
Profile
Kenjiro Kubota
istyle.inc
?PHP,?HHVM/Hack,?Javascript?
DTPDesigner
WebDesigner
FrontEnginner
WebApplicationEngiiner?now?
関心の分離とは
関心の分離(かんしんのぶんり、英語: separation of concerns、SoC)とは、ソフトウェア工
学において、プログラムを機能面において可能な限り重複がない、複数の機構に明確に分割
することをいう。ここでいう「関心?関心事?」とは、プログラムのある機能や振る舞い、目的
のことである。?Wikipedia?
Aspect?AOP?とは
アスペクト指向プログラミング(アスペクトしこうプログラミング、Aspect Oriented
Programming、AOP)は、オブジェクト指向ではうまく分離できない特徴(クラス間を横断
?cross‐cutting? するような機能)を「アスペクト」とみなし、アスペクト記述言語をもちいて
分離して記述することでプログラムに柔軟性をもたせようとする試み。アスペクトの例とし
ては、データ転送帯域の制限や例外の処理などがある。Java にアスペクト指向的要素を追加
したAspectJ が実験的に実装されている。?Wikipedia?
横断的関心事を解决する手段
代表的なケースはロギング、トランザクション、キャッシュなどが挙げられます
Laravelで利用する際はRay.Aopを用いている以下のライブラリがオススメ(宣伝)
ytake/laravel‐aspect
利用ケース ロギング
普通はこんな感じ
public?function?get($id)
{
????Log::info($id);
????$this‐>logger‐>info($id);
}
Logファサードを使ったり、コンストラクトインジェクションしたloggerを使ったり...
LaravelAspectを使うと
use?YtakeLaravelAspectAnnotationLoggable;
class?AspectLoggable
{
????/**
?????*?@Loggable
?????*?@param?null?$id
?????*?@return?null
?????*/
????public?function?normalLog($id?=?null)
????{
????????return?$id;
????}
}
利用ケース トランザクション
普通はこんな感じ?
public?function?save(array?$params)
{
????DB::beginTransaction();
????try?{
????????$result?=?$this‐>eloquent‐>save($params);
????????DB::commit();
????????return?$result;
????}?catch(Exception?$e)?{
????????DB::rollback();
????????Log::error($e‐>getMessage());
????????throw?$e;
????}
}
LaravelAspectなら
use?YtakeLaravelAspectAnnotationTransactional;
/**
?*?@Transactional("master")
?*?@LogExceptions
?*/
public?function?save(array?$params)
{
????return?$this‐>eloquent‐>save($params);
}
アノテーションで関心の分离ができる
他にもデフォルトで便利なアノテーションが用意されてます
詳しくはライブラリのREADEMEを見てください。
LaravelAspectに用意されてるアノテーションも便利なんですが
自分たちのアプリに合わせた独自のアノテーションを作り
たいですよね?
サンプルケース
最終ログイン日時の記録
まずはLaravelAspectを導入
$?composer?require?‐‐no‐update?ytake/laravel‐aspect?&&?composer?update
Provider登録
?config/app.php?
'providers'?=>?[
????//?added?AspectServiceProvider?
????YtakeLaravelAspectAspectServiceProvider::class,
????//?added?Artisan?Command
????YtakeLaravelAspectConsoleServiceProvider::class,
]
設定ファイルとかModulesをアプリケーションにコピー
$?php?artisan?ytake:aspect‐module‐publish
$?php?artisan?vendor:publish
こんな感じで追加されます
laravel
??‐?app
????‐?Modules
??????‐?CacheableModule.php
??????‐?CacheEvictModule.php
??????‐?CachePutModule.php
??????‐?TransactionalModule.php
??????‐?LoggableModule.php
??????‐?LogExceptionsModule.php
??????‐?PostConstructModule.php
??????‐?RetryOnFailureModule.php
??????
??‐?config
????‐?ytake‐laravel‐aop.php
独自アノテーションの定义
use?DoctrineCommonAnnotationsAnnotation;
/**
?*?@Annotation
?*?@Target("METHOD")
?*?Class?LastLoginTime
?*/
class?LastLoginTime?extends?Annotation
{
????//
}
アスペクト利用時は以下の様になる
‐> ?@LastLoginTime()?
インターセプターの定義
※ アノテーションが実行されるときの挙動を定義
use?RayAopMethodInvocation;
use?RayAopMethodInterceptor;
use?YtakeLaravelAspectAnnotationAnnotationReaderTrait;
class?LastLoginTimeInterceptor?implements?MethodInterceptor
{
????use?AnnotationReaderTrait;
????
????/**
?????*?@param?MethodInvocation?$invocation
?????*?@return?object
?????*/
????public?function?invoke(MethodInvocation?$invocation)
????{
????? //?before
????????$result?=?$invocation‐>proceed();
????????//?after
????????$user?=?$result‐>getUser();
????????$lastLoginTime?=?app()‐>make(AppServiceLastLoginTime::class);
????????if?(!$lastLoginTime‐>update($user))?{
????????????throw?new?ErrorException("Failed?Update?Last?Login?Time");
????????}
????????return?$result;
????}
}
//?before
$result?=?$invocation‐>proceed();
//?after
beforeはアノテーションを記述したメソッドが実行される前に実行される。
afterはメソッド実行されたあとに実行される
$resultにはそのメソッドの返り値が入ります。
ポイントカットの定義
※ 特定のアノテーションが実行されるときに起動されるインターセプターを定義
class?LastLoginTimePointCut?extends?CommonPointCut?implements?PointCutable
{
????/**?@var?string?*/
????protected?$annotation?=?AppAnnotationLastLoginTime::class;
????
????/**
?????*?@param?Container?$app
?????*?@return?RayAopPointcut
?????*/
????public?function?configure(Container?$app)
????{
????????$this‐>setInterceptor(new?LastLoginTimeInterceptor);
????????return?$this‐>withAnnotatedAnyInterceptor();
????}
}
モジュールの追加
※ アノテーションが利用されるクラスを定義する
use?AppAspectPointCutLastLoginTimePointCut;
use?YtakeLaravelAspectModulesAspectModule;
class?LastLoginTimeModule?extends?AspectModule
{
????/**?@var?array?*/
????protected?$classes?=?[
????????AppHttpControllerLoginController::class,
????];
????
????/**
?????*?@return?LastLoginTimePointCut
?????*/
????public?function?registerPointCut()
????{
????????return?new?LastLoginTimePointCut;
????}
}
最後にModulesを以下に登録してあげればOK
?config/ytake‐laravel‐aop.php?
????????'modules'?=>?[
????????????...
????????????//?append?modules
????????????AppModulesLastLoginTimeModule::class,
????????],
まとめ
LaravelAspectをとりあえず導入するだけでも楽々AOPが実現できる
拡張もできるので独自の要件にも対応できる
関心を分離することでいちクラスの責務を追求できるようになります
単一責任の原則
thanks :?

More Related Content

Laravel aspectで関心の分離