分析模式一


Martin04年寫的書,15年后的我看了之后,感覺之前看的書都白看了,哈哈!有點誇張,廢話不多說,開始!

1、案例一  團體

假設有個需求,讓你設計兩個類,一個是用戶類,一個是公司類你會怎么設計,大多數人會這么設計,代碼如下:

    public class User
    {
        public string UserName { get; set; }

        public string Adress { get; set; }

        public string Email { get; set; }
    }

    public class Company
    {
        public string CompanyName { get; set; }

        public string Adress { get; set; }

        public string Email { get; set; }
    }

ok,代碼能很好的完成需求,但是不完美,里面的Adress和Email是重復的概念.so,Martin引入了"團體"一詞,實際上就是對兩個類型進行了抽象,將重復的概念抽象到一個類中,代碼如下:

    /// <summary>
    /// 通過團體來封裝共有的屬性
    /// </summary>
    public class Group
    {
        public string Adress { get; set; }

        public string Email { get; set; }
    }

    public class User
    {
        public string UserName { get; set; }

        /// <summary>
        /// 這里通過值對象來實現共有的屬性,更合適,避免繼承的強依賴,而且在C#中只能單繼承
        /// 而且這樣的代碼更容易理解,表示User中存在一個團體類,里面封裝了該團體的所有屬性
        /// </summary>
        public Group Group { get; set; }
    }

    public class Company
    {
        public string CompanyName { get; set; }

        public Group Group { get; set; }
    }

類圖可以這樣表示.

 

2、案例二 組織層次

假設需要設計一個權限系統,該系統包含用戶、角色、權限、部門,大多數人聽到這個需求會這么編寫代碼,如下:

    /// <summary>
    /// 部門
    /// </summary>
    public class Department
    {
        public string DepartmentName { get; set; }

        public ICollection<Role> Roles { get; set; }
    }

    /// <summary>
    /// 角色
    /// </summary>
    public class Role
    {
        public string RoleName { get; set; }

        public ICollection<User> Users { get; set; }

        public ICollection<Action> Actions { get; set; }
    }

    /// <summary>
    /// 用戶
    /// </summary>
    public class User
    {
        public string UserName { get; set; }


    }

    /// <summary>
    /// 權限
    /// </summary>
    public class Action
    {
        public string ActionName { get; set; }
    }

ok,代碼能很好的完成需求,但是這個時候boss告訴你,我們不需要角色這個概念了,所有部門下的用戶一律平等,對boss來說他們都是員工,那么這個時候我們需要修改模型,修改模型往往是不好的,那么怎么規避這種操作呢?代碼如下:

    /// <summary>
    /// 部門
    /// </summary>
    public class Department: IDepartment
    {
        public string DepartmentName { get; set; }

        public ICollection<IRole> Roles { get; set; }
    }

    /// <summary>
    /// 角色
    /// </summary>
    public class Role: IRole
    {
        public string RoleName { get; set; }

        public ICollection<IUser> Users { get; set; }
    }

    /// <summary>
    /// 用戶
    /// </summary>
    public class User: IUser
    {
        public string UserName { get; set; }

        public ICollection<IAction> Actions { get; set; }
    }

    /// <summary>
    /// 權限
    /// </summary>
    public class Action: IAction
    {
        public string ActionName { get; set; }
    }

    public interface IDepartment { }

    public interface IRole { }

    public interface IUser { }

    public interface IAction { }

上面的代碼通過接口來約束層級關系,這個時候如果boss提出角色類(Role)不要了,那么我們不必修改模型,直接將關聯的約束修改掉,如下:

    /// <summary>
    /// 部門
    /// </summary>
    public class Department: IDepartment
    {
        public string DepartmentName { get; set; }

        public ICollection<IUser> Roles { get; set; }
    }

這個時候部門類就不再需要Role類了,但是它還是保存下來了,通過接口約束的轉變,部門類這個時候只需要用戶類就好了,通常情況,修改約束比修改模型結構要容易.

或者你可以像下面這樣:

    /// <summary>
    /// 用戶
    /// </summary>
    public class User: IUser, IRole
    {
        public string UserName { get; set; }

        public ICollection<IAction> Actions { get; set; }
    }

只需改變對應的實現,拿掉Role類,也能完成需求.總之修改約束跟靈活,雖然上面的例子可能不那么適合.

雖然通過接口約束,能解決上面的問題,但是上面的設計還是有問題,結構層次單一。

還是上面的例子,假設boss說為了滿足用戶的需求,我們需要給部門類增加單獨的權限,同時用戶類又持有對應的權限,那么這個時候權限就同時為部門類和用戶類負責,這個時候當一個用戶登陸進來.我們去數據庫查找到他對應的部門,然后拿到這個部門的所有權限,然后查到當前用戶對應的角色,將該角色下的所有權限拿出來和部門權限做一個並集,去重,得出最終的權限.那么隨着需求的增多,Action需要服務的層次增多,那么到最后代碼將變得難以理解,所以我們必須重構代碼.

 

重構版本,換一個例子,假設有一個子公司、區域子公司、部門、銷售辦事處、新增的零時服務小組,服務小組必須同時服務於部門、銷售辦事處,模型圖如下:

 

代碼如下:

    /// <summary>
    /// 子公司
    /// </summary>
    public class ChildCompany:INode
    {
        public string ChildCompanyName { get; set; }
    }


    /// <summary>
    /// 區域子公司
    /// </summary>
    public class AreaChildCompany : INode
    {
        public string ChildAreaChildCompany { get; set; }
    }

    /// <summary>
    /// 部門
    /// </summary>
    public class Department : INode
    {
        public string DepartmentName { get; set; }
    }

    /// <summary>
    /// 銷售辦事處
    /// </summary>
    public class SaleOffice: INode
    {
        public string SaleOfficeName { get; set; }
    }

    /// <summary>
    /// 新增的零時服務小組
    /// </summary>
    public class ServiceGroup : INode
    {

    }

    /// <summary>
    /// 組織節點接口
    /// </summary>
    public interface INode { }

    /// <summary>
    /// 組織
    /// </summary>
    public class Organization
    {
        /// <summary>
        /// 父節點
        /// </summary>
        public INode ParentNode;

        /// <summary>
        /// 子節點
        /// </summary>
        public INode ChildNode;

        /// <summary>
        /// 組織的開始生效時間
        /// </summary>
        public DateTime StartTime { get; set; } = DateTime.Now;

        /// <summary>
        /// 組織的生命結束期
        /// </summary>
        public DateTime EndTime { get; set; }
    }

    public class Pragram
    {
        static void Main(string[] args)
        {
            var organizationOne = new Organization()
            {
                ParentNode = new Department(),
                ChildNode = new ServiceGroup(),
                EndTime=DateTime.Now.AddDays(1)
            };

            var organizationTwo = new Organization()
            {
                ParentNode = new SaleOffice(),
                ChildNode = new ServiceGroup(),
                EndTime = DateTime.Now.AddDays(1)
            };

            //將兩個組織持久化到數據庫,這樣ServiceGroup就同時服務於Department部門和SaleOffice銷售辦事處
            //接着當零食服務小組登錄系統后,我們先去數據庫查到對應的沒有過期的組織對象,一般根據ServiceGroup的Guid去查找
            //拿到對應的服務對象后,調用該對象的通知方法,通知他們對應的信息

        }
    }

上面的代碼,將組織抽象成一種類型,通過數據庫持久化,這樣的形式去連接目標對象和服務對象.這樣就能將這種復雜的關聯關系從原先的強類型耦合種解放出來.這樣的設計更加的柔化,和不具有侵入性.所影響的范圍也更小,模型的改動也最小,基本是通過擴展的方式,而不是像上面的代碼那樣去修改模型種的代碼.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM