概要
書籍AdaptiveCodeで登場する述語デコレータパターンの使用事例。
述語デコレータはデコレータパターンの一つ。
cf: Decoratorパターン
条件付きの実行をクライアントから隠蔽するのに役立つ。
事例では条件として「ユーザーにアクセス権限があるか」、実行として「ファイル削除やダウンロードなどの操作」を想定した。
コード
権限を持つユーザーのみにファイル操作を実行する。
using System;
class Program
{
static void Main(string[] args)
{
User admin = new User() { Role = UserRole.Admin };
User readOnlyUser = new User() { Role = UserRole.ReadOnly };
//adminユーザーによるファイル消去を試行
IComponent adminFileDeleteAction = new PredicatedDecorator(
new FileDeleteAction(),
new UserHasDeleteAuthorityPredicate(
new DeleteAuthorityTester(admin)));
adminFileDeleteAction.Execute(); // -> successfully file deleted
//readOnlyユーザーによるファイル消去試行
IComponent ReadOnlyUserDeleteAction = new PredicatedDecorator(
new FileDeleteAction(),
new UserHasDeleteAuthorityPredicate(
new DeleteAuthorityTester(readOnlyUser)));
ReadOnlyUserDeleteAction.Execute(); // -> no authority
//readOnlyユーザーによるファイルダウンロード試行
IComponent ReadOnlyUserDownloadAction = new PredicatedDecorator(
new FileDownloadAction(),
new UserHasDownloadAuthorityPredicate(
new DownloadAuthorityTester(readOnlyUser)));
ReadOnlyUserDownloadAction.Execute(); // -> downloading file
}
}
public class User
{
public UserRole Role { get; set; }
}
public enum UserRole
{
Admin,
ReadWrite,
ReadOnly,
None
}
public interface IComponent
{
void Execute();
}
public class FileDeleteAction : IComponent
{
public void Execute()
{
Console.WriteLine("successfully file deleted");
}
}
public class FileDownloadAction : IComponent
{
public void Execute()
{
Console.WriteLine("downloading file");
}
}
public class PredicatedDecorator : IComponent
{
private readonly IComponent _component;
private readonly IPredicate _prediate;
public PredicatedDecorator(IComponent component, IPredicate predicate)
{
_component = component;
_prediate = predicate;
}
public void Execute()
{
if (_prediate.Test())
{
_component.Execute();
}
else
{
Console.WriteLine("no authority");
}
}
}
public interface IPredicate
{
bool Test();
}
public class UserHasDeleteAuthorityPredicate : IPredicate
{
public DeleteAuthorityTester _tester;
public UserHasDeleteAuthorityPredicate(DeleteAuthorityTester tester)
{
_tester = tester;
}
public bool Test()
{
return _tester.CheckAuthority();
}
}
public class UserHasDownloadAuthorityPredicate : IPredicate
{
private DownloadAuthorityTester _tester;
public UserHasDownloadAuthorityPredicate(DownloadAuthorityTester tester)
{
_tester = tester;
}
public bool Test()
{
return _tester.CheckAuthority();
}
}
public class DeleteAuthorityTester
{
private User _user;
public DeleteAuthorityTester(User user) {
_user = user;
}
public bool CheckAuthority()
{
if(_user.Role == UserRole.Admin)
{
return true;
}
else
{
return false;
}
}
}
public class DownloadAuthorityTester
{
private User _user;
public DownloadAuthorityTester(User user)
{
_user = user;
}
public bool CheckAuthority()
{
if(_user.Role == UserRole.None)
{
return false;
}
else
{
return true;
}
}
}
successfully file deleted
no authority
downloading file
クラス図
解説
前提知識
デコレータパターン
cf: Decoratorパターン
述語部分
述語デコレータの差分は、デコレータがフィールドに述語(Predicate)を持つ点。
述語部分により条件分岐させている。
デコレータ:PredicatedDecorator
述語部分: 下図
Adapterパターン
参照コードは述語デコレータパターンに加えて述語部分にObject Adapterパターンも適用している。
cf: C#でObject Adapterパターンの用途例
これはDeleteAuthorityTesterやDownloadAuthorityTesterが外部から与えられており、インターフェイスを作りたいがそれが難しいといったケースに使える。
Adapter(UserHadDownloadAuthorityやUserHAdDeleteAuthorityPredicate)でラップすることで実現している。