• [Architecture Pattern] Lazy Decoration


    动机 :

    在设计面向对象应用程序架构的时候,
    对象会包含相关的企业逻辑,而不是单纯的数据对象。
    但是当企业逻辑需要取得其他对象一起运算,如何「取得」是一件很复杂的事情。

    例如说:
    在系统内有一个「查询客户订单总金额」的企业逻辑,需要从系统取出客户的所有订单做金额加总。
    这个企业逻辑实作上可以分配到不同的对象,这边我们先定义这个企业逻辑是客户对象的职责。
    并用下列的程序代码,实作这个企业逻辑,
    这样的范例是可以正常的工作。

    但是换个场景会发现,在只是要编辑客户电话的时候,也需要取得订单查询接口。
    当系统越来越庞大,企业逻辑越来越多时,这个范例架构就会显得是个灾难。
    而且再细看的话会发现订单有参考到客户,这个范例有循环相依的问题。

    namespace ConsoleApplication001
    {
        public class Customer
        {
            public Guid Id { get; private set; }
    
            public string Name { get; set; }
    
            private readonly IOrderRepository _orderRepository = null;
    
            
            public Customer(Guid id, IOrderRepository orderRepository)
            {
                this.Id = id;
                this.Name = string.Empty;
                _orderRepository = orderRepository;
            }
    
    
            public int GetTotal()
            {
                int total = 0;
                foreach (Order order in _orderRepository.GetListByCustomer(this))
                {
                    total += order.Price;
                }
                return total;
            }
        }
    
        public class Order
        {
            public Guid Id { get; private set; }
    
            public Customer Customer { get; private set; }
    
            public int Price { get; set; }
    
    
            public Order(Guid id, Customer customer)
            {
                this.Id = id;
                this.Customer = customer;
                this.Price = 0;
            }
        }
    
        public interface IOrderRepository
        {
            IEnumerable<Order> GetListByCustomer(Customer customer);
        }
    }

    将系统重写成下列的程序代码,改由运作时将订单查询接口注入。
    这样的范例也是可以正常的工作,但是依然没有解决循环相依的问题。

    namespace ConsoleApplication002
    {
        public class Customer
        {
            public Guid Id { get; private set; }
    
            public string Name { get; set; }
    
    
            public Customer(Guid id)
            {
                this.Id = id;
                this.Name = string.Empty;
            }
    
    
            public int GetTotal(IOrderRepository orderRepository)
            {
                int total = 0;
                foreach (Order order in orderRepository.GetListByCustomer(this))
                {
                    total += order.Price;
                }
                return total;
            }
        }
    
        public class Order
        {
            public Guid Id { get; private set; }
    
            public Customer Customer { get; private set; }
    
            public int Price { get; set; }
    
    
            public Order(Guid id, Customer customer)
            {
                this.Id = id;
                this.Customer = customer;
                this.Price = 0;
            }
        }
    
        public interface IOrderRepository
        {
            IEnumerable<Order> GetListByCustomer(Customer customer);
        }
    }

    本文介绍一个『Lazy Decoration模式』。
    定义对象的职责跟规则,将对象与对象之间的相依性做切割。
    用来解决上列描述的问题。

    结构 :

    下图是这个架构的示意图。
    可以看到除了系统原本就有的客户、订单、订单查询接口之外,多了两个客户实体、客户实体工厂对象。

    订单到客户之间的相依,透过客户实体、客户实体工厂做了相依性切割。
    并且将「查询客户订单总金额」的企业逻辑,改分派到(客户实体)上。
    需要做「查询客户订单总金额」时,再建立(客户实体)来查询。
    而(客户实体)因为是继承自(客户)对象,在后续的应用,也可以直接将它当作(客户)来用。

    image

    实作 :

    文字写起来很复杂,其实看程序代码很简单。
    首先定义基本的(客户)、(订单)、(订单查询接口)这三个对象。
    要特别注意的是(客户)对象,它除了基本的建构函式之外,还包含了一个将自己当作参数的建构函式。
    这让继承的对象,不用关注属性增加、属性更名、属性值初始化...等等工作。

    namespace ConsoleApplication003
    {
        public class Customer
        {
            public Guid Id { get; private set; }
    
            public string Name { get; set; }
    
    
            public Customer(Guid id)
            {
                this.Id = id;
                this.Name = string.Empty;
            }
    
            public Customer(Customer item)
            {
                this.Id = item.Id;
                this.Name = item.Name;
            }
        }
    
        public class Order
        {
            public Guid Id { get; private set; }
    
            public Customer Customer { get; private set; }       
    
            public int Price { get; set; }        
    
    
            public Order(Guid id, Customer customer)
            {
                this.Id = id;
                this.Customer = customer;            
                this.Price = 0;
            }
        }
    
        public interface IOrderRepository
        {
            IEnumerable<Order> GetListByCustomer(Customer customer);
        }
    }

    再来看看(客户实体)对象,
    它继承了(客户)对象,并且实作了「查询客户订单总金额」这个企业逻辑。

    namespace ConsoleApplication003
    {
        public class CustomerEntity : Customer
        {
            private readonly IOrderRepository _orderRepository = null;
    
    
            public CustomerEntity(Customer item, IOrderRepository orderRepository)
                : base(item)
            {
                _orderRepository = orderRepository;
            }
    
            public int GetTotal()
            {
                int total = 0;
                foreach (Order order in _orderRepository.GetListByCustomer(this))
                {
                    total += order.Price;
                }
                return total;
            }
        }
    }

    最后是(客户实体工厂),
    它很简单的只是在建立(客户实体)时,将(订单查询接口)对象做注入的动作。

    namespace ConsoleApplication003
    {
        public class CustomerEntityFactory
        {
            private readonly IOrderRepository _orderRepository = null;
    
    
            public CustomerEntityFactory(IOrderRepository orderRepository)
            {
                _orderRepository = orderRepository;
            }
    
    
            public CustomerEntity Create(Customer item)
            {
                return new CustomerEntity(item, _orderRepository);
            }
        }
    }

    在这些对象整个建立完毕之后,
    当我们要做客户数据的新增、修改、删除、查询,直接将(客户)对象进出 Data Access Layer(DAL)。

    namespace ConsoleApplication003
    {
        class Test001
        {
            static void MainXXX(string[] args)
            {
                ICustomerRepository customerRepository = null; // 使用例如Spring.Net、Provider Pattern来反射生成。 
    
                foreach (Customer customer in customerRepository.GetAll())
                {
                    Console.WriteLine(customer.Name);
                }
            }
        }        
    }
    
    namespace ConsoleApplication003
    {
        public interface ICustomerRepository // Customer的DAL界面
        {
            Customer[] GetAll();
    
            Customer GetById(Guid id);
        }
    }

    当要查询某个客户的订单总金额时,建立(客户实体)就可以做查询。

    namespace ConsoleApplication003
    {
        class Test002
        {
            static void MainXXX(string[] args)
            {
                ICustomerRepository customerRepository = null; // 使用例如Spring.Net、Provider Pattern来反射生成。 
                IOrderRepository orderRepository = null;// 使用例如Spring.Net、Provider Pattern来反射生成。             
                CustomerEntityFactory customerEntityFactory = new CustomerEntityFactory(orderRepository);
    
                Customer customer = customerRepository.GetById(Guid.Parse("xxxxx"));
                CustomerEntity customerEntity = customerEntityFactory.Create(customer);
    
                Console.WriteLine(customerEntity.GetTotal());
            }
        }
    }
    
    namespace ConsoleApplication003
    {
        public interface ICustomerRepository // Customer的DAL界面
        {
            Customer[] GetAll();
    
            Customer GetById(Guid id);
        }
    }

    后记 :

    这个模式除了范例示范的企业逻辑分派为对象方法之外,也可以延伸成为对象属性、对象事件等等的功能。
    在实作的时候这个模式,也能将不同的企业逻辑做分类。例如 : CustomerQueryEntity、CustomerVerifyEntity。

    最后一提的是,这个模式是从 [Application Architecture] : Lazy Boundary 模式 所重整提取出来。
    当我们,
    将在开发软件项目的时候,遇到的各种不同功能面对象,归类并取一个好记的名字。
    反复重整功能面对象跟名词,最终就会产生属于自己的模式。 :D

  • 相关阅读:
    idea14导入eclipse项目并部署运行完整步骤
    Java之Socket
    Docker之宿主机ssh至docker容器
    ElasticSearch的安装、使用、踩坑
    Linux下grep、tail、wc、awk文件处理命令
    Spring中@Async注解实现“方法”的异步调用
    Thrift——栗子
    Linux中的守护进程——supervise
    【composer】 PHP composer 镜像地址更换
    【Mac】解决macos安装升级时报错安装所选更新时发生错误的问题
  • 原文地址:https://www.cnblogs.com/clark159/p/2205163.html
Copyright © 2020-2023  润新知