1
public enum CommercialInvoiceMode//商业发票样式
2
{
3
Duplicate, //一式两份
4
Triplicate, //一式三份
5
Quadruplicate, //一式四份
6
}
7![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
8
/// <summary>
9
/// 描述发票数据
10
/// </summary>
11
public class Invoice
12
{
13
/// <summary>
14
/// 打印发票
15
/// </summary>
16
/// <param name="commercialInvoiceMode">商业发票样式</param>
17
public void PrintInvoice(CommercialInvoiceMode commercialInvoiceMode)
18
{
19
}
20
}
这样的设计看似没有什么问题,用一个枚举可以描述发票不同的打印模式,仅提供一个PrintInvoice方法就可以实现对多种发票打印样式的处理,是不是自我感觉很良好啊?![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
13
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
14
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
15
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
17
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
19
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
20
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
不过这样的设计有些致命的缺点:
具体的实现打印的代码结构大致如下
1
/// <summary>
2
/// 描述发票数据
3
/// </summary>
4
public class Invoice
5
{
6
/// <summary>
7
/// 打印发票
8
/// </summary>
9
/// <param name="commercialInvoiceMode">商业发票样式</param>
10
public void PrintInvoice(CommercialInvoiceMode commercialInvoiceMode)
11
{
12
switch(commercialInvoiceMode)
13
{
14
case CommercialInvoiceMode.Decuplicate:
15
break;
16
case CommercialInvoiceMode.Triplicate:
17
break;
18
case CommercialInvoiceMode.Quadruplicate:
19
break;
20
}
21
}
22
}
如果你对以上的代码没有什么歧义,那就说明你对类的开闭原则不是很了解。![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
13
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
14
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
15
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
17
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
19
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
21
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
22
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
为什么这样的设计不好呢?
比如用户要求有了新的打印样式
我们要修改枚举
1
public enum CommercialInvoiceMode//商业发票样式
2
{
3
Duplicate, //一式两份
4
Triplicate, //一式三份
5
Quadruplicate, //一式四份
6
Quintuplicate, //一式五份
7
Sextuplicate,//一式六份
8
Septuplicate,//一式七份
9
Octuplicate,//一式八份
10
Nonuplicate, //一式九份
11
Decuplicate,//一式十份
12
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
修改了枚举,就必须要修改PrintInvoice方法中的switch代码,因为多了发票样式,就必须重新的修改类。重新的修改枚举,典型的破坏类的封装。
如果,你还是不同意这样的设计破坏了封装,那我们再考虑一个场景:要是以上的打印样式分别是两个客户提出的,难道你要两个枚举?
1
public enum CommercialInvoiceModeA//商业发票样式
2
{
3
Septuplicate,//一式七份
4
Octuplicate,//一式八份
5
Nonuplicate, //一式九份
6
Decuplicate,//一式十份
7
}
8![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
9
public enum CommercialInvoiceModeB//商业发票样式
10
{
11
Sextuplicate,//一式六份
12
Septuplicate,//一式七份
13
Octuplicate,//一式八份
14
Nonuplicate, //一式九份
15
Decuplicate,//一式十份
16
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
13
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
14
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
15
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
16
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
如果有这两个枚举,那PrintInvoice方法马上要完全改变,这个类就不能通用了。必须为不同的客户提供不同的Invoice类,假设Invoice的数据和行为都一样,那就是说必须为了InvoiceStyle而实现不同的类。
而这些InvoiceStyleA和InvoiceStyleB类仅仅是打印的样式不同,其他的数据和行为都一样,所以这样的设计不完全合理。
我们需要一个Invoice类,该类的PrintInvoice能在不改变Invoice类的实现情况下,可以灵活的处理打印。
我们先设计一个发票打印类:
1
public class PrintInvoice
2
{
3
//一式两份
4
public static void DuplicateStyle(Invoice data)
5
{
6
}
7
//一式三份
8
public static void TriplicateStyle(Invoice data)
9
{
10
}
11
//一式四份
12
public static void QuadruplicateStyle(Invoice data)
13
{
14
}
15
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
13
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
14
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
15
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
这个打印类,为每种打印提供了具体的实现,将来有了新的打印样式,仅仅是增加新的方法(可以直接为类增加或继承后增加),没有破坏类的开闭原则。
那这个类可以帮我们做什么呢?
我们观察一下,这几个方法的外观其实是一致的:同样的返回值,同样的参数列表。
让你想到了什么?接口?恩,是的,想想如果用接口的知识,我们现在能做些什么呢?
我们一定会设计如下的类:
1
public interface IPrintInvoice
2
{
3
void PrintInvoice(Invoice data);
4
}
5![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
6
public class DuplicateStyle : IPrintInvoice
7
{
8
public void PrintInvoice(Invoice data)
9
{
10
11
}
12
}
13![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
14![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
15
public class TriplicateStyle : IPrintInvoice
16
{
17
public void PrintInvoice(Invoice data)
18
{
19![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20
}
21
}
22![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
23![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
24
public class QuadruplicateStyle : IPrintInvoice
25
{
26
public void PrintInvoice(Invoice data)
27
{
28![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
29
}
30
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
12
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
13
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
14
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
15
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
16
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
17
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
18
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
19
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
20
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
21
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
22
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
23
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
24
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
25
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
26
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
27
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
28
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
29
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
30
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
然后我们的Invoice类如下设计:
1
public class Invoice
2
{
3
public void PrintInvoice(IPrintInvoice printStyle)
4
{
5
printStyle.PrintInvoice(this);
6
}
7
}
这个Invoice类设计的就非常的漂亮,符合开闭原则了。不过,不足的就是要写很多IPrintInvoice的实现类,每个类就一个PrintInvoice方法,这样的话,类实在太多了。不方便管理。![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
所以我们还是在我们先前的PrintInvoice类打主意。
C#提供了一种特殊的引用类型:委托。这个类型可以把我们的方法(函数)包装为独立的对象。然后就可以把这个包装后的方法(委托)当参数用。这个参数具有被包装函数的所有特征:有返回值和参数。就是说该包装后的对象其实就是一个方法(函数)。
我们来看一下,委托的声明。
1
public delegate void PrintStyle(Invoice data);
就一行,没有{}的,在类外面声明(也就是说委托的级别和类、接口的级别是一样的)。![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
这个委托描述了,他可以帮助返回为void,参数为Invoice类型的函数,具体函数的名字它不管,它只要返回值和参数列表符合他的包装要求就可以了。
我们修改Invoice的PrintInvoice方法
1
/// <summary>
2
/// 描述发票数据
3
/// </summary>
4
public class Invoice
5
{
6![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
public void PrintInvoice(PrintStyle printStyle)
8
{
9
printStyle(this);
10
}
11
}
看看,第9行是不是用起来很接近接口的用法啊?参数PrintStyle方法传入的就是外观和delegate void PrintStyle(Invoice data)一致的函数,用这个PrintStyle就是调用一个函数。![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
7
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
8
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
9
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
10
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif)
11
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockEnd.gif)
看看具体的调用
1
Invoice invoice = new Invoice();
2
invoice.PrintInvoice(new PrintStyle(PrintInvoice.QuadruplicateStyle));
上面的代码就是把PrintInvoice的QuadruplicateStyle方法传递给了Invoice类的PrintInvoice,那么前端代码的第9行其实就是调用PrintInvoice的QuadruplicateStyle方法。![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
1
Invoice invoice = new Invoice();
2
invoice.PrintInvoice(new PrintStyle(PrintInvoice.QuadruplicateStyle));
3
PrintStyle printStyle = new PrintStyle(PrintInvoice.TriplicateStyle);
4
invoice.PrintInvoice(printStyle);
5
printStyle = new PrintStyle(PrintInvoice.DuplicateStyle);
6
invoice.PrintInvoice(printStyle);
委托再次提醒我们在类设计时的一个真理:直接的是最不稳定的,间接的才是最稳定的![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
2
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
3
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
4
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
5
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
6
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)