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

## 概要
書籍[AdaptiveCode](https://honto.jp/ebook/pd-contents_0629112617.html)で登場する述語デコレータパターンの使用事例。

述語デコレータはデコレータパターンの一つ。
cf: [Decoratorパターン](https://qiita.com/umezu_y/items/2f2950258bda0415cbc6)
条件付きの実行をクライアントから隠蔽するのに役立つ。

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

## コード
権限を持つユーザーのみにファイル操作を実行する。
~~~c#: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;
        }
    }
}
~~~

~~~c#:出力
successfully file deleted
no authority
downloading file
~~~

## クラス図
![スクリーンショット 2023-04-13 10.03.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14345/f0c0fb7c-8124-8ad3-5548-42059ec91bc8.png)

## 解説
### 前提知識
デコレータパターン
cf: [Decoratorパターン](https://qiita.com/umezu_y/items/2f2950258bda0415cbc6)

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

デコレータ：PredicatedDecorator
述語部分: 下図
![スクリーンショット 2023-04-13 10.41.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14345/08f0048a-b7bd-61b8-91db-e7f130fc2811.png)

### Adapterパターン
参照コードは述語デコレータパターンに加えて述語部分にObject Adapterパターンも適用している。
cf: [C#でObject Adapterパターンの用途例
](https://qiita.com/umezu_y/items/d54473c5bc90ef26c3ce)
これはDeleteAuthorityTesterやDownloadAuthorityTesterが外部から与えられており、インターフェイスを作りたいがそれが難しいといったケースに使える。
Adapter（UserHadDownloadAuthorityやUserHAdDeleteAuthorityPredicate）でラップすることで実現している。