委托,语言级别的设计模式

我们有个发票类,需要提供一个打印的方法,客户告诉我们,这个订单要提供多种打印的样式,那么我们一般会这样设计
 1None.gif    public enum CommercialInvoiceMode//商业发票样式
 2ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 3InBlock.gif        Duplicate, //一式两份 
 4InBlock.gif        Triplicate, //一式三份 
 5InBlock.gif        Quadruplicate, //一式四份 
 6ExpandedBlockEnd.gif    }

 7None.gif
 8ExpandedBlockStart.gifContractedBlock.gif    /**//// <summary>
 9InBlock.gif    ///  描述发票数据
10ExpandedBlockEnd.gif    /// </summary>

11None.gif    public class Invoice
12ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
13ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
14InBlock.gif        /// 打印发票
15InBlock.gif        /// </summary>
16ExpandedSubBlockEnd.gif        /// <param name="commercialInvoiceMode">商业发票样式</param>

17InBlock.gif        public void PrintInvoice(CommercialInvoiceMode commercialInvoiceMode)
18ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
19ExpandedSubBlockEnd.gif        }

20ExpandedBlockEnd.gif    }
这样的设计看似没有什么问题,用一个枚举可以描述发票不同的打印模式,仅提供一个PrintInvoice方法就可以实现对多种发票打印样式的处理,是不是自我感觉很良好啊?
不过这样的设计有些致命的缺点:
具体的实现打印的代码结构大致如下
 1ExpandedBlockStart.gifContractedBlock.gif    /**//// <summary>
 2InBlock.gif    ///  描述发票数据
 3ExpandedBlockEnd.gif    /// </summary>

 4None.gif    public class Invoice
 5ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 6ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 7InBlock.gif        /// 打印发票
 8InBlock.gif        /// </summary>
 9ExpandedSubBlockEnd.gif        /// <param name="commercialInvoiceMode">商业发票样式</param>

10InBlock.gif        public void PrintInvoice(CommercialInvoiceMode commercialInvoiceMode)
11ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
12InBlock.gif            switch(commercialInvoiceMode)
13ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
14InBlock.gif                case CommercialInvoiceMode.Decuplicate:
15InBlock.gif                    break;
16InBlock.gif                case CommercialInvoiceMode.Triplicate:
17InBlock.gif                    break;
18InBlock.gif                case CommercialInvoiceMode.Quadruplicate:
19InBlock.gif                    break;
20ExpandedSubBlockEnd.gif            }

21ExpandedSubBlockEnd.gif        }

22ExpandedBlockEnd.gif    }
如果你对以上的代码没有什么歧义,那就说明你对类的开闭原则不是很了解。
为什么这样的设计不好呢?
比如用户要求有了新的打印样式
我们要修改枚举
 1None.gif    public enum CommercialInvoiceMode//商业发票样式
 2ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 3InBlock.gif        Duplicate, //一式两份 
 4InBlock.gif        Triplicate, //一式三份 
 5InBlock.gif        Quadruplicate, //一式四份 
 6InBlock.gif        Quintuplicate, //一式五份 
 7InBlock.gif        Sextuplicate,//一式六份 
 8InBlock.gif        Septuplicate,//一式七份 
 9InBlock.gif        Octuplicate,//一式八份 
10InBlock.gif       Nonuplicate, //一式九份 
11InBlock.gif       Decuplicate,//一式十份
12ExpandedBlockEnd.gif    }

修改了枚举,就必须要修改PrintInvoice方法中的switch代码,因为多了发票样式,就必须重新的修改类。重新的修改枚举,典型的破坏类的封装。
如果,你还是不同意这样的设计破坏了封装,那我们再考虑一个场景:要是以上的打印样式分别是两个客户提出的,难道你要两个枚举?
 1None.gif    public enum CommercialInvoiceModeA//商业发票样式
 2ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 3InBlock.gif        Septuplicate,//一式七份 
 4InBlock.gif        Octuplicate,//一式八份 
 5InBlock.gif        Nonuplicate, //一式九份 
 6InBlock.gif        Decuplicate,//一式十份
 7ExpandedBlockEnd.gif    }

 8None.gif
 9None.gif    public enum CommercialInvoiceModeB//商业发票样式
10ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
11InBlock.gif        Sextuplicate,//一式六份 
12InBlock.gif        Septuplicate,//一式七份 
13InBlock.gif        Octuplicate,//一式八份 
14InBlock.gif        Nonuplicate, //一式九份 
15InBlock.gif        Decuplicate,//一式十份
16ExpandedBlockEnd.gif    }

如果有这两个枚举,那PrintInvoice方法马上要完全改变,这个类就不能通用了。必须为不同的客户提供不同的Invoice类,假设Invoice的数据和行为都一样,那就是说必须为了InvoiceStyle而实现不同的类。
而这些InvoiceStyleA和InvoiceStyleB类仅仅是打印的样式不同,其他的数据和行为都一样,所以这样的设计不完全合理。
我们需要一个Invoice类,该类的PrintInvoice能在不改变Invoice类的实现情况下,可以灵活的处理打印。
我们先设计一个发票打印类:
 1None.gif    public class PrintInvoice
 2ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 3InBlock.gif        //一式两份
 4InBlock.gif        public static void DuplicateStyle(Invoice data)
 5ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 6ExpandedSubBlockEnd.gif        }

 7InBlock.gif        //一式三份
 8InBlock.gif        public static void TriplicateStyle(Invoice data)
 9ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
10ExpandedSubBlockEnd.gif        }

11InBlock.gif        //一式四份
12InBlock.gif        public static void QuadruplicateStyle(Invoice data)
13ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
14ExpandedSubBlockEnd.gif        }

15ExpandedBlockEnd.gif    }

这个打印类,为每种打印提供了具体的实现,将来有了新的打印样式,仅仅是增加新的方法(可以直接为类增加或继承后增加),没有破坏类的开闭原则。
那这个类可以帮我们做什么呢?
我们观察一下,这几个方法的外观其实是一致的:同样的返回值,同样的参数列表。
让你想到了什么?接口?恩,是的,想想如果用接口的知识,我们现在能做些什么呢?
我们一定会设计如下的类:
 1None.gif    public interface IPrintInvoice
 2ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 3InBlock.gif        void PrintInvoice(Invoice data);
 4ExpandedBlockEnd.gif    }

 5None.gif
 6None.gif    public class DuplicateStyle : IPrintInvoice
 7ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 8InBlock.gif        public void PrintInvoice(Invoice data)
 9ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif
10InBlock.gif        
11ExpandedSubBlockEnd.gif        }

12ExpandedBlockEnd.gif    }

13None.gif
14None.gif
15None.gif    public class TriplicateStyle : IPrintInvoice
16ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
17InBlock.gif        public void PrintInvoice(Invoice data)
18ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
19InBlock.gif
20ExpandedSubBlockEnd.gif        }

21ExpandedBlockEnd.gif    }

22None.gif
23None.gif
24None.gif    public class QuadruplicateStyle : IPrintInvoice
25ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
26InBlock.gif        public void PrintInvoice(Invoice data)
27ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
28InBlock.gif
29ExpandedSubBlockEnd.gif        }

30ExpandedBlockEnd.gif    }

然后我们的Invoice类如下设计:
1None.gif    public class Invoice
2ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
3InBlock.gif        public void PrintInvoice(IPrintInvoice printStyle)
4ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
5InBlock.gif            printStyle.PrintInvoice(this);
6ExpandedSubBlockEnd.gif        }

7ExpandedBlockEnd.gif    }
这个Invoice类设计的就非常的漂亮,符合开闭原则了。不过,不足的就是要写很多IPrintInvoice的实现类,每个类就一个PrintInvoice方法,这样的话,类实在太多了。不方便管理。
所以我们还是在我们先前的PrintInvoice类打主意。
C#提供了一种特殊的引用类型:委托。这个类型可以把我们的方法(函数)包装为独立的对象。然后就可以把这个包装后的方法(委托)当参数用。这个参数具有被包装函数的所有特征:有返回值和参数。就是说该包装后的对象其实就是一个方法(函数)。
我们来看一下,委托的声明。
1None.gifpublic delegate void PrintStyle(Invoice data);
就一行,没有{}的,在类外面声明(也就是说委托的级别和类、接口的级别是一样的)。
这个委托描述了,他可以帮助返回为void,参数为Invoice类型的函数,具体函数的名字它不管,它只要返回值和参数列表符合他的包装要求就可以了。
我们修改Invoice的PrintInvoice方法
 1ExpandedBlockStart.gifContractedBlock.gif    /**//// <summary>
 2InBlock.gif    ///  描述发票数据
 3ExpandedBlockEnd.gif    /// </summary>
 4None.gif    public class Invoice
 5ExpandedBlockStart.gifContractedBlock.gif    dot.gif{
 6InBlock.gif
 7InBlock.gif        public void PrintInvoice(PrintStyle printStyle)
 8ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 9InBlock.gif            printStyle(this);
10ExpandedSubBlockEnd.gif        }

11ExpandedBlockEnd.gif    }
看看,第9行是不是用起来很接近接口的用法啊?参数PrintStyle方法传入的就是外观和delegate void PrintStyle(Invoice data)一致的函数,用这个PrintStyle就是调用一个函数。
看看具体的调用
1None.gif            Invoice invoice = new Invoice();
2None.gif            invoice.PrintInvoice(new PrintStyle(PrintInvoice.QuadruplicateStyle));
上面的代码就是把PrintInvoice的QuadruplicateStyle方法传递给了Invoice类的PrintInvoice,那么前端代码的第9行其实就是调用PrintInvoice的QuadruplicateStyle方法。
1None.gif            Invoice invoice = new Invoice();
2None.gif            invoice.PrintInvoice(new PrintStyle(PrintInvoice.QuadruplicateStyle));
3None.gif            PrintStyle printStyle = new PrintStyle(PrintInvoice.TriplicateStyle);
4None.gif            invoice.PrintInvoice(printStyle);
5None.gif            printStyle = new PrintStyle(PrintInvoice.DuplicateStyle);
6None.gif            invoice.PrintInvoice(printStyle);
委托再次提醒我们在类设计时的一个真理:直接的是最不稳定的,间接的才是最稳定的

转载于:https://www.cnblogs.com/shyleoking/archive/2007/02/23/654563.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值