• [Architecture Pattern] Entity Expansion


    动机 :

    一个软件系统的生命周期,必然面临到系统改版的问题。而在系统改版的时候,最常遇到的问题之一是,用户希望增加系统对象的数据字段(例如 : 用户数据增加相片)。常见的做法是把相关的功能,从把整个系统从UI到DB重整(重写?)一遍,让用户希望增加至系统的字段,在系统里实现。

    这样的做法,笔者把它称作『修改式程序代码累积』。所谓的修改式程序代码累积是说,藉由修改经过验证、并且正常运作的程序代码与接口来扩充系统。理论上,程序代码经过修改之后,必须重新执行完整的测试。而接口经过修改,使用手册、教育训练等等,常常也必需做同步的更新。可以说是牵一发动全身。

    笔者比较喜爱『增加式程序代码累积』。所谓的增加式程序代码累积是说,原有的程序代码、包含接口不做更动,而是增加额外的程序代码附加到系统内来扩充系统。 因为程序代码与接口都没有做过修改,就可以避免修改程序代码所产生的额外工作。但不可避免的,这样的系统在开发初期要花比较大的心力做设计。

    本文介绍一个Entity Expansion模式。Entity Expansion模式主要是定义一组,数据对象(Entity)以及边界对象(Repository)的生成、结构、行为模式,用来扩展对象的属性数据。实作这个模式,可以为系统加入增加式程序代码累积的能力。

    * 下列文章分别标注段落为01、02只是不想全部写成一个超长段落,不是示意项目必须拆解成两个。

    基础平台 :

    结构

    参与者

    Page Shell
    -页面的壳层,可以透过设定数据,动态挂载系统页面的系统。

    范例程序

    Page Shell依照开发平台的不同,会有不同的选择。例如以ASP.NET的平台来说,可以直接套用ASP.NET的页面机制,然后再另外建立一个索引页面,用修改索引页面的方式来达成动态挂载系统页面的需求。

    基础项目01 :

    结构

    参与者

    UserManagePage
    -User管理页面,提供新增、修改、删除、查询 User的功能。
    -使用例如 Spring.Net、Provider Pattern来反射生成 IUserRepositoryFactory。
    -使用生成的 IUserRepositoryFactory生成 IUserRepository。
    -使用 IUserRepository新增、修改、删除、查询 User。

    User
    -系统运作使用的数据对象。

    IUserRepositoryFactory
    -生成 IUserRepository。
    -依靠例如 Spring.Net、Provider Pattern来反射生成。

    IUserRepository
    -数据对象 User进出系统边界的接口。
    -提供新增、修改、删除、查询等功能。

    范例程序

    namespace CLK.EntityExpansion
    {
        public class UserManagePage
        {
            // Methods
            public void ShowUser()
            {
                // GetData
                IUserRepositoryFactory userRepositoryFactory = null; // 使用例如Spring.Net、Provider Pattern来反射生成。(Base专案生成 DefaultUserRepositoryFactory、Ex专案生成 ExpandedUserRepositoryFactory)
    
                IUserRepository userRepository = userRepositoryFactory.CreateRepository();
    
                IEnumerable<User> userCollection = userRepository.GetAll();
    
                // Show
                this.ShowUser(userCollection);
            }
    
            private void ShowUser(IEnumerable<User> userCollection)
            {
                //.....
            }
        }
    
        public class User
        {
            // Constructor
            public User()
            {
                this.UserID = Guid.Empty;
                this.Name = string.Empty;
                this.Description = string.Empty;
            }
    
            protected User(User item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
                this.UserID = item.UserID;
                this.Name = item.Name;
                this.Description = item.Description;
            }
    
    
            // Properties
            public Guid UserID { get; set; }
    
            public string Name { get; set; }
    
            public string Description { get; set; }        
        }
    
        public interface IUserRepositoryFactory
        {
            // Methods
            IUserRepository CreateRepository();
        }
    
        public interface IUserRepository
        {
            // Methods
            void Add(User item);
    
            void Modify(User item);
    
            void Remove(Guid id);
    
            User GetByID(Guid id);
    
            IEnumerable<User> GetAll();
        }
    }
    

    基础项目02 :

    结构

    参与者

    DefaultUserRepositoryFactory
    -IUserRepositoryFactory的实作
    -生成 IUserRepository - SqlUserRepository。

    SqlUserRepository
    -数据对象 User进出系统边界的接口,IUserRepository的实作。
    -依靠 IUserRepositoryFactory生成。
    -负责将数据对象User进出SQL数据库。

    范例程序

    namespace CLK.EntityExpansion
    {
        public class DefaultUserRepositoryFactory : IUserRepositoryFactory
        {
            // Methods
            public IUserRepository CreateRepository()
            {
                // Create
                IUserRepository userRepository = new SqlUserRepository();
    
                // Return
                return userRepository;
            }
        }
    
        public class SqlUserRepository : IUserRepository
        {
            // Methods
            public void Add(User item)
            {
                // Sql操作...
            }
    
            public void Modify(User item)
            {
                // Sql操作...
            }
    
            public void Remove(Guid id)
            {
                // Sql操作...
            }
    
            public User GetByID(Guid id)
            {
                // Sql操作...
                return null;
            }
    
            public IEnumerable<User> GetAll()
            {
                // Sql操作...
                return null;
            }
        }
    }
    

    扩展专案01 :

    结构

    参与者

    ExUserManagePage
    -ExUser管理页面,提供新增、修改、删除、查询 ExUser的功能。
    -使用例如 Spring.Net、Provider Pattern来反射生成 IExUserRepositoryFactory。
    -使用生成的 IExUserRepositoryFactory生成 IExUserRepository。
    -使用 IExUserRepository新增、修改、删除、查询 ExUser。

    ExUser
    -系统运作使用的数据对象。
    -继承User并且扩展自己的属性数据。

    IUserRepositoryFactory
    -生成 IExUserRepository。
    -依靠例如 Spring.Net、Provider Pattern来反射生成。

    IExUserRepository
    -数据对象 ExUser进出系统边界的接口。
    -提供新增、修改、删除、查询等功能。

    范例程序

    namespace CLK.EntityExpansion.Expanded
    {
        public class ExUserManagePage
        {
            // Methods
            public void ShowExUser()
            {
                // GetData
                IExUserRepositoryFactory userRepositoryFactory = null; // 使用例如Spring.Net、Provider Pattern来反射生成。(Base专案生成 DefaultExUserRepositoryFactory、Ex专案生成 ExpandedExUserRepositoryFactory)
    
                IExUserRepository userRepository = userRepositoryFactory.CreateRepository();
    
                IEnumerable<ExUser> userCollection = userRepository.GetAll();
    
                // Show
                this.ShowExUser(userCollection);
            }
    
            private void ShowExUser(IEnumerable<ExUser> exUserCollection)
            {
                //.....
            }
        }
    
        public class ExUser : User
        {        
            // Constructor
            public ExUser()
                : base()
            {
                this.Photo = null;
            }
    
            public ExUser(User item)
                : base(item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
                this.Photo = null;
            }
    
    
            // Properties
            public byte[] Photo { get; set; }
        }
    
        public interface IExUserRepositoryFactory
        {
            // Methods
            IExUserRepository CreateRepository();
        }
    
        public interface IExUserRepository
        {
            // Methods
            void Add(ExUser item);
    
            void Modify(ExUser item);
    
            void Remove(Guid id);
    
            ExUser GetByID(Guid id);
    
            IEnumerable<ExUser> GetAll();
        }
    }
    

    扩展专案02 :

    结构

    参与者

    DefaultExUserRepositoryFactory
    -IExUserRepositoryFactory的实作。
    -生成 IUserRepository - SqlUserRepository。
    -生成 IExUserSectionRepository - SqlExUserSectionRepository。
    -生成 ExUserRepository,传入生成的IUserRepository、IExUserSectionRepository。

    ExUserRepository
    -数据对象 ExUser进出系统边界的接口,IExUserRepository的实作。
    -依靠 DefaultExUserRepositoryFactory生成,接受传入的IUserRepository、IExUserSectionRepository。
    -负责数据对象ExUser拆解为 User及ExUserSection。并将使用者对其新增修改删除动作拆解为,User对象进出IUserRepository、ExUserSection物件进出IExUserSectionRepository。
    -负责数据对象User及ExUserSection组合为ExUser。并将使用者对其查询动作拆解为,User对象进出IUserRepository、ExUserSection物件进出IExUserSectionRepository。


    ExUserSection
    -系统运作使用的数据对象,存放ExUser扩充增加的属性数据。

    IExUserSectionRepository
    -数据对象 ExUserSection进出系统边界的接口。
    -提供新增、修改、删除、查询等功能。

    SqlExUserSectionRepository
    -数据对象ExUserSection 进出系统边界的接口,IUserRepository的实作。
    -负责将数据对象 ExUserSection进出SQL数据库。


    ExpandedUserRepositoryFactory
    -IUserRepositoryFactory的实作。
    -生成 IUserRepository - SqlUserRepository。
    -生成 IExUserSectionRepository - SqlExUserSectionRepository。
    -生成 UserRepositoryDecorator传入生成的IUserRepository、IExUserSectionRepository。

    UserRepositoryDecorator
    -数据对象 User进出系统边界的接口,IUserRepository的实作。
    -依靠 IUserRepositoryFactory生成。
    -依靠 ExpandedUserRepositoryFactory生成,接受传入的IUserRepository、IExUserSectionRepository。
    -负责将用户对数据对象User新增修改删除查询动作拆解为进出IUserRepository。
    -负责数据对象User拆解为 UserId。并将使用者对其删除动作拆解为,ExUserSection对象自IExUserSectionRepository移除。

    范例程序

    namespace CLK.EntityExpansion.Expanded
    {
        public class ExUserSection
        {
            // Properties
            public Guid UserID { get; set; }
    
            public byte[] Photo { get; set; }
        }
    
        public interface IExUserSectionRepository
        {
            // Methods
            void Add(ExUserSection item);
    
            void Modify(ExUserSection item);
    
            void Remove(Guid id);
    
            ExUserSection GetByID(Guid id);
        }
    
        public class SqlExUserSectionRepository : IExUserSectionRepository
        {
            public void Add(ExUserSection item)
            {
                // Sql操作...
            }
    
            public void Modify(ExUserSection item)
            {
                // Sql操作...
            }
    
            public void Remove(Guid id)
            {
                // Sql操作...
            }
    
            public ExUserSection GetByID(Guid id)
            {
                // Sql操作...
                return null;
            }
        }
    }
    
    namespace CLK.EntityExpansion.Expanded
    {
        public class DefaultExUserRepositoryFactory : IExUserRepositoryFactory
        {
            // Methods
            public IExUserRepository CreateRepository()
            {
                // Create
                IUserRepository userRepository = new SqlUserRepository();
                IExUserSectionRepository exUserSectionRepository = new SqlExUserSectionRepository();
    
                // Return
                return new ExUserRepository(userRepository, exUserSectionRepository);
            }
        }
    
        public class ExUserRepository : IExUserRepository
        {
            // Fields
            private readonly IUserRepository _userRepository = null;
    
            private readonly IExUserSectionRepository _exUserSectionRepository = null;
            
    
            // Constructor
            public ExUserRepository(IUserRepository userRepository, IExUserSectionRepository exUserSectionRepository)
            {
                #region Require
    
                if (userRepository == null) throw new ArgumentNullException();
                if (exUserSectionRepository == null) throw new ArgumentNullException();
    
                #endregion
                _userRepository = userRepository;
                _exUserSectionRepository = exUserSectionRepository;
            }
    
    
            // Methods
            public void Add(ExUser item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
    
                // User
                _userRepository.Add(item);
    
                // ExUserSection
                ExUserSection exUserSection = new ExUserSection();
                exUserSection.UserID = item.UserID;
                exUserSection.Photo = item.Photo;
                _exUserSectionRepository.Add(exUserSection);
            }
    
            public void Modify(ExUser item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
    
                // User
                _userRepository.Modify(item);
    
                // ExUserSection
                ExUserSection exUserSection = new ExUserSection();
                exUserSection.UserID = item.UserID;
                exUserSection.Photo = item.Photo;
                _exUserSectionRepository.Modify(exUserSection);
            }
    
            public void Remove(Guid id)
            {
                #region Require
    
                if (id == Guid.Empty) throw new ArgumentNullException();
    
                #endregion
    
                // User            
                _userRepository.Remove(id);
    
                // ExUserSection
                _exUserSectionRepository.Remove(id);
            }
    
            public ExUser GetByID(Guid id)
            {
                #region Require
    
                if (id == Guid.Empty) throw new ArgumentNullException();
    
                #endregion
    
                // User    
                User user = _userRepository.GetByID(id);
                if (user == null) return null;
    
                // ExUserSection
                ExUserSection exUserSection = _exUserSectionRepository.GetByID(id);
                if (exUserSection == null) return new ExUser(user);
    
                // ExUser
                ExUser exUser = new ExUser(user);
                exUser.Photo = exUserSection.Photo;
                return exUser;
            }
    
            public IEnumerable<ExUser> GetAll()
            {
                // Result
                List<ExUser> exUserList = new List<ExUser>();
    
                // User    
                foreach (User user in _userRepository.GetAll())
                {
                    // ExUserSection
                    ExUserSection exUserSection = _exUserSectionRepository.GetByID(user.UserID);
                    if (exUserSection == null)
                    {
                        exUserList.Add(new ExUser(user));
                        continue;
                    }
    
                    // ExUser
                    ExUser exUser = new ExUser(user);
                    exUser.Photo = exUserSection.Photo;
                    exUserList.Add(exUser);
                }                      
    
                // Return
                return exUserList;
            }
        }    
    }
    
    namespace CLK.EntityExpansion.Expanded
    {
        public class ExpandedUserRepositoryFactory : IUserRepositoryFactory
        {
            // Methods
            public IUserRepository CreateRepository()
            {
                // Create
                IUserRepository userRepository = new SqlUserRepository();
                IExUserSectionRepository exUserSectionRepository = new SqlExUserSectionRepository();
    
                // Return
                return new UserRepositoryDecorator(userRepository, exUserSectionRepository);
            }
        }
    
        public class UserRepositoryDecorator : IUserRepository
        {
            // Fields
            private readonly IUserRepository _userRepository = null;
    
            private readonly IExUserSectionRepository _exUserSectionRepository = null;
    
    
            // Constructor
            public UserRepositoryDecorator(IUserRepository userRepository, IExUserSectionRepository exUserSectionRepository)
            {
                #region Require
    
                if (userRepository == null) throw new ArgumentNullException();
                if (exUserSectionRepository == null) throw new ArgumentNullException();
    
                #endregion
                _userRepository = userRepository;
                _exUserSectionRepository = exUserSectionRepository;
            }
    
    
            // Methods
            public void Add(User item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
    
                // User
                _userRepository.Add(item);
            }
    
            public void Modify(User item)
            {
                #region Require
    
                if (item == null) throw new ArgumentNullException();
    
                #endregion
    
                // User
                _userRepository.Modify(item);
            }
    
            public void Remove(Guid id)
            {
                #region Require
    
                if (id == Guid.Empty) throw new ArgumentNullException();
    
                #endregion
    
                // User            
                _userRepository.Remove(id);
    
                // ExUserSection
                _exUserSectionRepository.Remove(id);
            }
    
            public User GetByID(Guid id)
            {
                #region Require
    
                if (id == Guid.Empty) throw new ArgumentNullException();
    
                #endregion
    
                // User    
                User user = _userRepository.GetByID(id);
    
                // Return
                return user;
            }
    
            public IEnumerable<User> GetAll()
            {
                // Result
                IEnumerable<User> userList = null;
    
                // User    
                userList = _userRepository.GetAll();
    
                // Return
                return userList;
            }
        }
    }
    

    结语 :

    本文范例简单的说就是,当基础项目与扩展项目都建立完毕。在原本基础项目的管理页面建立User对象,在扩展项目的ExUser管理页面也可以查询到新增的ExUser(内容为User数据,ExUser扩展的属性数据为默认值)。当在扩展项目删除ExUser时,基础项目内的User也会同时被删除。

    本文介绍的Entity Expansion模式,看起来只是扩展了对象的属性数据,但其实可以看成,处理了强型别扩展对象进出系统边界的责任。以此模式为基础发展,理论上可以设计出能无限延伸的应用系统架构。


  • 相关阅读:
    day10 作业
    文件操作
    字符编码
    元组、字典、集合内置方法, 深浅拷贝
    day07作业
    一周总结
    mysql操作进阶
    mysql操作篇续
    mysql-操作篇
    mysql的安装
  • 原文地址:https://www.cnblogs.com/clark159/p/2205136.html
Copyright © 2020-2023  润新知