zoukankan      html  css  js  c++  java
  • 分析模式一

    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去查找
                //拿到对应的服务对象后,调用该对象的通知方法,通知他们对应的信息
    
            }
        }

    上面的代码,将组织抽象成一种类型,通过数据库持久化,这样的形式去连接目标对象和服务对象.这样就能将这种复杂的关联关系从原先的强类型耦合种解放出来.这样的设计更加的柔化,和不具有侵入性.所影响的范围也更小,模型的改动也最小,基本是通过扩展的方式,而不是像上面的代码那样去修改模型种的代码.

  • 相关阅读:
    OpenCV笔记——cvFloodFill漫水填充算法
    C# 总结 随笔
    MYSQL存储过程 随笔
    MYSQL总结 随笔
    xPath 总结 随笔
    Javascript 总结 随笔
    linux中top查看cpu使用率超过100%
    页面优化小记1
    基于数据库的多语言解决方案
    基于消息队列的日志组件
  • 原文地址:https://www.cnblogs.com/GreenLeaves/p/10236791.html
Copyright © 2011-2022 走看看