[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

原文地址:https://www.cnblogs.com/clark159/p/2205163.html