Attribute使用した保守性の上がるコードの書き方

はじめに

始めまして、エンジニアの朴・小林です。
ここでは保守性が高まる.NETのAttribute(アトリビュート)について説明していきます。

Attribute(アトリビュート)とは?

属性とも呼ばれる、クラス・構造体、または、そのメンバー(メゾット・プロパティなど)に対して付与できる追加情報です。属性をプログラム要素に関連付けると、リフレクションと呼ばれる手法を使用して、実行時にその属性を照会することができます。

例えば:PersonというクラスにHand、Foot、Face等のプロパティが存在し、Walk(),Eat()等のメソッドが存在する。

Personというクラスに[police]というAttributeを付けると、このPersonが警察だとわかる、同様に[engineer]を付けると、このPersonがエンジニアだと分かるというイメージを持っていただければと思います。

Attributeの使い方

■Attributeはクラス                                 Attributeを使用する場合、必ずSystem.Attribute.を継承する必要がある

■Attributeの名前                           一般的に、〇〇Attributeとする。                     利用する際にAttributeを省略することができる。
例:TestAttributeは利用時に[Test]と省略ができる

アトリビュートを実装してみる

例えば、あるクラスのUserが下記の状態だとする。
Normalの場合は「通常ユーザー」を出力
Frozenの場合は「利用中止ユーザー」を出力
Deletedの場合は「退会ユーザー」を出力

public enum UserState
{
    Nomal = 0, //通常
    Frozen = 1, //利用できない
    Deleted = 2 //退会済
}

一般的に、下記の様なswitch文のロジックを組むと思いますが、
これだと、状態を追加するほど、分岐の条件も増やさないといけないので、保守性も非常い低いです。
ここでAttributeを利用すればこれらの事が解決できます。

switch(state)
{
    case UserState.Nomal:
        Console.WriteLine("通常ユーザー");
        break;

    case User.Frozen:
        Console.WriteLine("利用中止ユーザー");
        break;

     case User.Deleted:
        Console.WriteLine("退会ユーザー");
        break;
}

↓Attributeを使用すると

//アトリビュートクラスを作成
public class RemarkAttribute : Attribute
{
    public RemarkAttribute(string remark)
    {
        this._Remark = remark;
    }

    private string _Remark = null;

    public string GetRemark()
    {
        return this._Remark;
    }
}

//Remarkのアトリビュートを追加
public enum UserState
{
    [Remark("通常ユーザー")]
    Nomal = 0, //通常
    [Remark("利用中止ユーザー")]
    Frozen = 1, //利用できない
    [Remark("退会ユーザー")]
    Deleted = 2 //退会済
}

//アトリビュートの機能を作成
public static class RemarkExtension
{
    public static string GetRemark(this Enum value)
    {
        //反射、リフレクションを利用。ネットで調べてください
     //valueのタイプを取得
        Type type = value.GetType();
        //フィールドを取得
        FieldInfo field = type.GetField(value.ToString());
     //フィールドにRemarkAttributeが定義されていれば
        if (field.IsDefined(typeof(RemarkAttribute), true))
        {
            RemarkAttribute attribute =
                (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true);
            return attribute.GetRemark();
        }
        else
        {
            return value.ToString();
        }
    }
}

上記を実現すると、下記のようなコードがいらなくなる。

switch(state)
{
    case UserState.Nomal:
        Console.WriteLine("通常ユーザー");
        break;

    case User.Frozen:
        Console.WriteLine("利用中止ユーザー");
        break;

     case User.Deleted:
        Console.WriteLine("退会ユーザー");
        break;
}

//stateがRemarkアトリビュートがある場合、アトリビュートのRemarkをそのまま取得できる、ない場合は、stateそのままになる。
Console.WriteLine(state.GetRemark());

また、UserクラスにAgeというプロパティが存在していて、データベースへ挿入する際に、年齢が18から30までのユーザーしか挿入させないように制限したい。

まず、AgeValidationAttributeというクラスを宣言

public class AgeValidationAttribute : Attribute
{
    public AgeValidationAttribute(int min, int max)
    {
        this._Min = min;
        this._Max = _Max;
    }

    private int _Min = 0;
    private int _Max = 0;

    //バリデーション
    public bool Validate(object value)
    {
        if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
        {
            if (int.TryParse(value.ToString(), out int resut))
            {
                if (resut > this._Min && resut < this._Max)
                {
                    return true;
                }
            }
        }

        return false;
    }
}

次はアトリビュートの機能を実現するためのStaticメソッドを宣言

public static class ValidateExtension
{
    public static bool Validate(this object value)
    {
        Type type = value.GetType();
        foreach (var prop in type.GetProperties())
        {
            if (prop.IsDefined(typeof(AgeValidationAttribute), true))
            {
                AgeValidationAttribute attribute =
                    (AgeValidationAttribute)prop.GetCustomAttribute(typeof(RemarkAttribute), true);
                if (attribute.Validate(prop.GetValue(value)) == false)
                {
                    return false;
                }
            }
        }
        return true;
    }
}

最後に、プロパティAgeにAgeValidationアトリビュートを追加

[AgeValidation(18,30)]
public int Age { get; set; }

利用時

user.Validate();

最後に

アトリビュートを使用すると保守性の高いコードを実現することができます。 是非利用してみてください。

関連記事

プロジェクトストーリー

おすすめ記事

技術

コメント

この記事へのコメントはありません。

カテゴリー

TOP
TOP