Encapsulation - Property

  • 1468
  • 0

Value Object, value type, Property , struct , Null object, Immutable object

寫程式時,常常會用到屬性來對外開放存取

現況


public string DisplayName
{
       get
       {
             return this._version;
       }
       set
       {
             this._version = value;
       }
}

如果今天需要在 set value 時作驗證與判斷


public string DisplayName
{
       get
       {
             return this._version;
       }
       set
       {
             if (value == null)
             {
                      throw new ArgumentNullException("value")
             }
             this._version = value;
       }
}

再進一步,在特殊情況,給定一個default value


public string DisplayName
{
       get
       {
             return this._version;
       }
       set
       {
             var temp = value;
             if (temp == null)
             {
                      temp  = String.Empty;
             }
             this._version = temp ;
       }
}

上述的程式碼雖然帶來便利,但是會讓使用者帶來混亂,假設使用者set null value,所以他會預期get value時,回傳null

結果得到一個String.Empty,Amazing!

 

該怎麼辦?

Encapsulation是王道。

讓我們試著把驗證規則、預設值等等封裝成Immutable Object


public class OrganizationVersion
{
        
        private readonly string _version;

        public OrganizationVersion(string pVersion)
        {
            // 驗證規則 預設值 轉換格式等等
            if (pVersion == null)
            {
                  throw new ArgumentNullException("pVersion");
            }
           
            this._version = pVersion;
        }

        public string DisplayName
        {
            get
            {
                return this._version;
            }
        }
}

public  class Test
{
      public Version DisplayName
      {
             get;
             set;
      }
}

但是這樣依然需要判斷null的情況

我們需要Null Object Pattern

改成使用struct實作,再加上一點輔助功能

 

/// <summary>
    /// Version
    /// </summary>
    internal struct Version
    {
        /// <summary>
        /// version display name
        /// </summary>
        private string _version;
 
        /// <summary>
        /// 是否已設值
        /// </summary>
        private readonly bool _flag;
 
        /// <summary>
        /// ctor
        /// </summary>
        /// <param name="pVersion">version</param>
        public Version(string pVersion)
        {
            //驗證規則
            this._version = pVersion;
            this._flag = true;
        }
 
        /// <summary>
        /// vesrion property
        /// </summary>
        public string DisplayName
        {
            get
            {
                if (!this._flag)
                {
                    this._version = "DefaultValue";
                }
                return this._version;
            }
        }
 
        /// <summary>
        /// implicit 轉換 Versionto string
        /// </summary>
        /// <param name="pVersion">version</param>
        /// <returns>displayname</returns>
        public static implicit operator string(Version pVersion)
        {
            return pVersion.DisplayName;
        }
 
        /// <summary>
        /// explicit 轉換 string to Version
        /// </summary>
        /// <param name="pDisplayName">version displayname</param>
        /// <returns>Version</returns>
        public static explicit operator Version(string pDisplayName)
        {
            return new Version(pDisplayName);
        }
 
        /// <summary>
        /// override tostring
        /// </summary>
        /// <returns>displayname</returns>
        public override string ToString()
        {
            return this._version;
        }
 
        /// <summary>
        /// override GetHashCode
        /// </summary>
        /// <returns>int</returns>
        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }
    }

結果


var test = new Test();

//預設值
string displayName = test.DisplayName.DisplayName;

test.DisplayName = new Version("TestDisplayName");

//displayName = "TestDisplayName"
displayName  = test.DisplayName.DisplayName;

//implicit, displayName = "TestDisplayName"
displayName  = test.DisplayName;

//displayName = "TestDisplayName"
displayName  = test.DisplayName.ToString();

好處

減少重複程式碼

封裝驗證規則與資料

提供預設值

避免null判斷

其他

http://blog.ploeh.dk/2011/05/26/CodeSmellAutomaticProperty.aspx

http://grabbagoft.blogspot.com/2007/12/dealing-with-primitive-obsession.html

分享