LoginSignup
0
1

More than 1 year has passed since last update.

C#で述語デコレータパターンの使用事例

Posted at

概要

書籍AdaptiveCodeで登場する述語デコレータパターンの使用事例。

述語デコレータはデコレータパターンの一つ。
cf: Decoratorパターン
条件付きの実行をクライアントから隠蔽するのに役立つ。

事例では条件として「ユーザーにアクセス権限があるか」、実行として「ファイル削除やダウンロードなどの操作」を想定した。

コード

権限を持つユーザーのみにファイル操作を実行する。

example.cs
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

クラス図

スクリーンショット 2023-04-13 10.03.08.png

解説

前提知識

デコレータパターン
cf: Decoratorパターン

述語部分

述語デコレータの差分は、デコレータがフィールドに述語(Predicate)を持つ点。
述語部分により条件分岐させている。

デコレータ:PredicatedDecorator
述語部分: 下図
スクリーンショット 2023-04-13 10.41.00.png

Adapterパターン

参照コードは述語デコレータパターンに加えて述語部分にObject Adapterパターンも適用している。
cf: C#でObject Adapterパターンの用途例

これはDeleteAuthorityTesterやDownloadAuthorityTesterが外部から与えられており、インターフェイスを作りたいがそれが難しいといったケースに使える。
Adapter(UserHadDownloadAuthorityやUserHAdDeleteAuthorityPredicate)でラップすることで実現している。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1