C#设计模式前言

C#设计模式(1)

http://www.cnblogs.com/zhenyulu/articles/36058.aspx

课本:《C#设计模式》,电子工业出版社,ISBN 7-5053-8979-3。33元含光盘。

课程内容:设计模式

来源:亚历山大的建筑模式、Gamma等人(1995)创作的"Design Patterns: Elements of Reusable Software"。这本书通常被称作"Gang of Four"或"GoF",开创性的创造了《设计模式》。

也有人说"三十六计"就是"模式"。


一、 C# 面向对象程序设计复习

点击http://files.cnblogs.com/zhenyulu/CSharp.rar下载,内容包括:

字段与属性.cs
属性、方法作用范围.cs
一加到一百.cs
使用接口排序(2).cs
使用接口排序(1).cs
求质数.cs
冒泡法排序.cs
九九表.cs
静态与非静态.cs
构造函数.cs
方法重载.cs
多态性.cs
递归求阶乘.cs
打印三角形.cs
传值调用与引用调用.cs

二、 设计模式举例

在设计模式中有一种模式叫Builder模式,其原理如下:

我们可以将Builder理解成电饭锅,给这个Builder放进去米和水,经过Builder的Build后,我们就可以取出香喷喷的米饭了。
C#中有一个类叫StringBuilder,输入必要的信息后,就可以取出对应的String。其使用方法如下:

using System;
using System.Text;

class Exam
{
publicstatic void Main()
{
StringBuilder sb
= new StringBuilder();
sb.Append(
'a',2);
sb.Append(
'b',3);
sb.Append(
'c',4);
Console.WriteLine(sb.ToString());
//打印出 aabbbcccc
sb.Remove(0, sb.Length);//清除sb中的所有信息
}

}

程序执行结果为: aabbbcccc
请使用StringBuilder对以下打印三角型的程序进行改写,写出新程序。

using System;
public class Exam
{
publicstatic void Main()
{
Console.Write(
"请输入行数:");
int lines= int.Parse(Console.ReadLine());
Console.WriteLine(
"");
for(int i=1; i<=lines ; i++)
{
for(int k=1; k<= lines-i; k++)
Console.Write(
"");
for(int j=1; j<=i*2-1; j++)
Console.Write(
"*");
Console.WriteLine(
"");
}

}

}

答:

using System;
using System.Text;
class Exam
{
publicstatic void Main()
{
Console.Write(
"请输入行数:");
int lines= int.Parse(Console.ReadLine());
Console.WriteLine(
"");

StringBuilder sb
= new StringBuilder();

for(int i=1; i<=lines ; i++)
{
sb.Append(
'', lines-i);
sb.Append(
'*', i*2-1);
Console.WriteLine(sb.ToString());
sb.Remove(
0, sb.Length);
}

}

}

三、 先有鸡还是先有蛋?

到底是先有鸡还是先有蛋?看下面的代码:

using System;

class Client
{
publicstatic void Main ()
{
Base b
= new Base();
Derived d
= new Derived();
b.d
= d;
Console.WriteLine(b.d.m);
}

}


class Base
{
publicint n = 9;
public Derived d;
}


class Derived : Base
{
publicint m = 10;
}

Derived继承自Base,可以说没有Base就没有Derived,可Base里面有一个成员是Derived类型。到底是先有鸡还是先有蛋?这个程序可以正常编译执行并打印结果10。

四、 大瓶子套小瓶子还是小瓶子套大瓶子?

另外一个例子:

using System;

class Client
{
publicstatic void Main ()
{
A a
= new A();
B b
= new B();
a.b
= b;
b.a
= a;
}

}


class A
{
public B b;
}


class B
{
public A a;
}

上面的代码似乎描述了"a包含b,b包含a"的关系,到底是大瓶子套小瓶子还是小瓶子套大瓶子呢?

五、 .net本质

关于"先有鸡还是先有蛋"的程序,系统运行后,内存结构如下:
C#设计模式前言_第1张图片

由图中可以看出,根本不存在鸡与蛋的问题,而是型与值的问题以及指针引用的问题。

关于"大瓶子套小瓶子还是小瓶子套大瓶子"问题,系统运行后,内存结构如下:

C#设计模式前言_第2张图片

由于是指针引用,所以也无所谓大瓶子还是小瓶子了。

关于更多内容可以参考《.NET本质论 第1卷:公共语言运行库》。


参考文献:
阎宏,《Java与模式》,电子工业出版社
[美]James W. Cooper,《C#设计模式》,电子工业出版社
[美]Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社
[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社
http://www.dofactory.com/Patterns/Patterns.aspx


C#设计模式(2)

http://www.cnblogs.com/zhenyulu/articles/36061.aspx

《人月神话》焦油坑、没有银弹

* 软件腐化的原因:

问题所在 设计目标
----------------------------------------------------------------------------
过于僵硬 可扩展性(新性能可以很容易加入系统)
过于脆弱 灵活性(修改不会波及其它)
复用率低
粘度过高 可插入性(新功能容易加入系统(气囊加入方向盘))

* 提高系统可复用性的几点原则:
传统复用:
1. 代码的粘帖复用
2. 算法的复用
3. 数据结构的复用

* 可维护性与可复用性并不完全一致

* 对可维护性的支持:


一、 "开放-封闭"原则(OCP)

Open-Closed Principle原则讲的是:一个软件实体应当对扩展开放,对修改关闭。

优点:
通过扩展已有软件系统,可以提供新的行为,以满足对软件的新的需求,使变化中的软件有一定的适应性和灵活性。
已有软件模块,特别是最重要的抽象层模块不能再修改,这使变化中的软件系统有一定的稳定性和延续性。

例子:玉帝招安美猴王
当年大闹天宫便是美猴王对玉帝的新挑战。美猴王说:"'皇帝轮流做,明年到我家。'只教他搬出去,将天宫让于我!"对于这项挑战,太白金星给玉皇大帝提出的建议是:"降一道招安圣旨,宣上界来…,一则不劳师动众,二则收仙有道也。"

换而言之,不劳师动众、不破坏天规便是"闭",收仙有道便是"开"。招安之道便是玉帝天庭的"开放-封闭"原则。

C#设计模式前言_第3张图片

招安之法的关键便是不允许更改现有的天庭秩序,但允许将妖猴纳入现有秩序中,从而扩展了这一秩序。用面向对象的语言来讲,不允许更改的是系统的抽象层,而允许更改的是系统的实现层。


二、 里氏代换原则(LSP)

Liskov Substitution Principle(里氏代换原则):子类型(subtype)必须能够替换它们的基类型。

白马、黑马
C#设计模式前言_第4张图片

反过来的代换不成立
《墨子·小取》说:"娣,美人也,爱娣,非爱美人也……"娣便是妹妹,哥哥喜爱妹妹,是因为两人是兄妹关系,而不是因为妹妹是个美人。因此,喜爱妹妹不等同于喜爱美人。用面向对象语言描述,美人是基类,妹妹是美人的子类。哥哥作为一个有"喜爱()"方法,接受妹妹作为参数。那么,这个"喜爱()"方法一般不能接受美人的实例。

C#设计模式前言_第5张图片

一个违反LSP的简单例子(长方形和正方形)

public class Rectangle
{
privatelong width;
privatelong height;

publicvoid setWidth(long width)
{
this.width= width;
}

publiclong getWidth()
{
returnthis.width;
}

publicvoid setHeight(long height)
{
this.height= height;
}

publiclong getHeight()
{
returnthis.height;
}

}


public class Square
{
privatelong side;

publicvoid setSide(long side)
{
this.side= side;
}


publiclong getSide()
{
return side;
}

}


正方形不可以做长方形的子类

using System;

public class Rectangle
{
privatelong width;
privatelong height;

publicvoid setWidth(long width)
{
this.width= width;
}

publiclong getWidth()
{
returnthis.width;
}

publicvoid setHeight(long height)
{
this.height= height;
}

publiclong getHeight()
{
returnthis.height;
}

}


public class Square : Rectangle
{
privatelong side;

publicvoid setWidth(long width)
{
setSide(width);
}


publiclong getWidth()
{
return getSide();
}


publicvoid setHeight(long height)
{
setSide(height);
}


publiclong getHeight()
{
return getSide();
}


publiclong getSide()
{
return side;
}


publicvoid setSide(long side)
{
this.side= side;
}

}


public class SmartTest
{
publicvoid resize(Rectangle r)
{
while (r.getHeight()>= r.getWidth() )
{
r.setWidth(r.getWidth()
+1);
}

}

}

C#设计模式前言_第6张图片
在执行SmartTest的resize方法时,如果传入的是长方形对象,当高度大于宽度时,会自动增加宽度直到超出高度。但是如果传入的是正方形对象,则会陷入死循环。

代码重构

public interface Quadrangle
{
publiclong getWidth();
publiclong getHeight();
}


public class Rectangle : Quadrangle
{
privatelong width;
privatelong height;

publicvoid setWidth(long width)
{
this.width= width;
}

publiclong getWidth()
{
returnthis.width;
}

publicvoid setHeight(long height)
{
this.height= height;
}

publiclong getHeight()
{
returnthis.height;
}

}


public class Square : Quadrangle
{
privatelong side;

publicvoid setSide(long side)
{
this.side= side;
}


publiclong getSide()
{
return side;
}


publiclong getWidth()
{
return getSide();
}


publiclong getHeight()
{
return getSide();
}

}

C#设计模式前言_第7张图片


参考文献:

阎宏,《Java与模式》,电子工业出版社

[美]James W. Cooper,《C#设计模式》,电子工业出版社

[美]Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社

[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社

[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社
http://www.dofactory.com/Patterns/Patterns.aspx

C#设计模式(3)

三、 依赖倒置原则(DIP)

依赖倒置(Dependence Inversion Principle)原则讲的是:要依赖于抽象,不要依赖于具体。

简单的说,依赖倒置原则要求客户端依赖于抽象耦合。原则表述:

抽象不应当依赖于细节;细节应当依赖于抽象;
要针对接口编程,不针对实现编程。

反面例子:



缺点:耦合太紧密,Light发生变化将影响ToggleSwitch。

解决办法一:
将Light作成Abstract,然后具体类继承自Light。

C#设计模式前言_第8张图片

优点:ToggleSwitch依赖于抽象类Light,具有更高的稳定性,而BulbLight与TubeLight继承自Light,可以根据"开放-封闭"原则进行扩展。只要Light不发生变化,BulbLight与TubeLight的变化就不会波及ToggleSwitch。

缺点:如果用ToggleSwitch控制一台电视就很困难了。总不能让TV继承自Light吧。

解决方法二:
C#设计模式前言_第9张图片

优点:更为通用、更为稳定。

结论:
使用传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。

四、 接口隔离原则(ISP)

接口隔离原则(Interface Segregation Principle)讲的是:使用多个专门的接口比使用单一的总接口总要好。换而言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小接口上的。

过于臃肿的接口是对接口的污染。不应该强迫客户依赖于它们不用的方法。

My object-oriented umbrella(摘自Design Patterns Explained)

Let me tell you about my great umbrella. It is large enough to get into! In fact, three or four other people can get in it with me. While we are in it, staying out of the rain, I can move it from one place to another. It has a stereo system to keep me entertained while I stay dry. Amazingly enough, it can also condition the air to make it warmer or colder. It is one cool umbrella.

My umbrella is convenient. It sits there waiting for me. It has wheels on it so that I do not have to carry it around. I don't even have to push it because it can propel itself. Sometimes, I will open the top of my umbrella to let in the sun. (Why I am using my umbrella when it is sunny outside is beyond me!)

In Seattle, there are hundreds of thousands of these umbrellas in all kinds of colors. Most people call them cars.

实现方法:
1、 使用委托分离接口
2、 使用多重继承分离接口

五、 合成/聚合复用原则(CARP)

合成/聚合复用原则(Composite/Aggregate Reuse Principle或CARP)经常又叫做合成复用原则(Composite Reuse Principle或CRP),就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新对象通过向这些对象的委派达到复用已有功能的目的。

简而言之,要尽量使用合成/聚合,尽量不要使用继承。

o Design to interfaces.
o Favor composition over inheritance.
o Find what varies and encapsulate it.
(摘自:Design Patterns Explained)

区分"Has-A"与"Is-A"

"Is-A"是严格的分类学意义上定义,意思是一个类是另一个类的"一种"。而"Has-A"则不同,它表示某一个角色具有某一项责任。

导致错误的使用继承而不是合成/聚合的一个常见的原因是错误的把"Has-A"当作"Is-A"。

例如:
C#设计模式前言_第10张图片

实际上,雇员、经理、学生描述的是一种角色,比如一个人是"经理"必然是"雇员",另外一个人可能是"学生雇员",在上面的设计中,一个人无法同时拥有多个角色,是"雇员"就不能再是"学生"了,这显然是不合理的。

错误源于把"角色"的等级结构与"人"的等级结构混淆起来,误把"Has-A"当作"Is-A"。解决办法:

C#设计模式前言_第11张图片

六、 迪米特法则(LoD)

迪米特法则(Law of Demeter或简写LoD)又叫最少知识原则(Least Knowledge Principle或简写为LKP),也就是说,一个对象应当对其它对象有尽可能少的了解。

其它表述:
只与你直接的朋友们通信
不要跟"陌生人"说话
每一个软件单位对其它的单位都只有最少的知识,而且局限于那些与本单位密切相关的软件单位。

迪米特法则与设计模式
Facade模式、Mediator模式

使民无知
《老子》第三章曰:"是以圣人之治,虚其心,实其腹,弱其志,常使民无知无欲。"使被"统治"的对象"愚昧"化,处于"无知"的状态,可以使"统治"的成本降低。
所谓"最少知识"原则,实际上便是老子的"使民无知"的统治之术。

不相往来
《老子》云:"小国寡民……邻国相望,鸡犬之声相闻,民至老死,不相往来。"将被统治的对象隔离开来,使它们没有直接的通信,可以达到分化瓦解,继而分而治之的效果。迪米特法则与老子的"小国寡民"的统治之术不谋而合。


参考文献:
阎宏,《Java与模式》,电子工业出版社
[美]James W. Cooper,《C#设计模式》,电子工业出版社
[美]Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社
[美]Robert C. Martin,《敏捷软件开发-原则、模式与实践》,清华大学出版社
[美]Don Box, Chris Sells,《.NET本质论 第1卷:公共语言运行库》,中国电力出版社
http://www.dofactory.com/Patterns/Patterns.aspx


OO与设计模式的原则、目标

http://www.cnblogs.com/anlyren/archive/2008/01/31/oo.html

前两天,和一朋友聊到OO设计原则时,对设计模式有了更深的了解,在这里总结一下,与大家分享。
OO(Object–Oriented )面向对象 
  OO方法(Object-Oriented Method,面向对象方法,面向对象的方法)是一种把面向对象的思想应用于软件开发过程中,指导开发活动的系统方法,简称OO (Object-Oriented)方法,是建立在“对象”概念基础上的方法学。对象是由数据和容许的操作组成的封装体,与客观实体有直接对应关系,一个对象类定义了具有相似性质的一组对象。而每继承性是对具有层次关系的类的属性和操作进行共享的一种方式。所谓面向对象就是基于对象概念,以对象为中心,以类和继承为构造机制,来认识、理解、刻画客观世界和设计、构建相应的软件系统。

OO的设计目标:

  • 可扩展性:有了新的需求,新的性能可以容易添加到系统中,不影响现有的性能,也不会带来新的缺陷。
  • 可修改性:系统一部分的代码要修改时不会破坏系统的现有结构,也不会影响到其它的部分。
  • 可替换性:可以将系统中的某些代码替换为相同接口的其它类,不会影响到系统。

设计模式的设计原则:

  • “开放--封闭”原则:
    设计模式的核心原则。软件实体(类,模块,函数)对于扩展是开放的,对于修改是关闭的 。实现开闭原则的关键就是抽象化。“开放--封闭”原则中,不允许修改的是抽象的类或者接口。允许扩展的是具体的实现类,抽象类和接口在“开-闭”原则中扮演着极其重要的角色 。
  • 封装变化点原则:
    这是对"开-闭"原则最好的实现..不要把你的可变因素放在多个类中,或者散落在程序的各个角落..你应该将可变的因素,封套起来..并且切忌不要把所用的可变因素封套在一起..最好的解决办法是,分块封套你的可变因素!!避免超大类,超长类,超长方法的出现!!给你的程序增加艺术气息,将程序艺术化是我们的目标!!
  • 里氏代换原则:
    任何基类可以出现的地方,子类也可以出现 。
  • 依赖倒转原则:
    要依赖抽象,而不要依赖具体的实现。
    抽象不应当依赖于细节,细节应当依赖于抽象;要针对接口编程,不要针对实现编程
  • 单一职责原则:
    一个类应该只有一个引起它变化的原因。
  • 接口隔离法则 :
    为了做到尽可能小的耦合性,我们需要使用接口来规范类,用接口来约束类。要达到迪米特法则的要求,最好就是实现接口隔离法则。
    使用多个专门的接口比使用单一的总接口要好.从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小的接口上的。一个接口应当简单地代表一个角色,而不是多个角色。多个演员可以同时演一个角色,就象孙悟空的孩儿一样。
  • 合成/聚合原则:
    要尽量使用合成/聚合原则,而不是继承关系达到软件复用的目的。聚合用来表示“拥有”或整体与部分的关系,而合成则用来表示一种强得多的“拥有”关系。在一个合成关系里,部分和整体的生命周期是一样的。合成就象所说的“合成品”,拆开就坏。
  • 迪米特法则:
    系统中的类,尽量不要与其他类互相作用,减少类之间的耦合度。
    一个对象应当对其它对象有尽可能少的了解,两个类不必直接通信,可以通过第三者(抽象)转发这个调用,这个抽象的第三者可以是门面,可以是调停者,甚至是一个抽象类或接口,模块要独立,独立被封装,他们只靠public的API来通信,只要有可能,一个类应当设计成不变类,其属性都应该是私有的,如果一个类有太多的public访问权限的方法,可以考虑使用多个类把一个类的私有方法和公有方法分开。

聚合(Aggregation):

   这是一种松散的对象间的关系.举个例子:计算机和他的外围设备就是一例.

  用来表示拥有关系或者整体与部分的关系。

组合(Composition):

这是一种非常强的对象间的关系,举个例子,树和它的树叶之间的关系.

在一个合成里,部分与整体的生命周期都是一样的。一个合成的新对象完全拥有对其组成

部分的支配权。包括他们的创建和毁灭。

最后总结一下:

聚合:

  •  聚合有时能够不依赖部分而存在,有时又不能
  • 部分可以独立于聚合而存在
  • 如果有一部分遗失,聚合会给人一种不完全的感觉
  • 部分的所有权可以由几个聚合来共享,比如打印机

合成:

  • 部分某一时刻只能属于某一个组成
  • 组成唯一的负责处理它的所有部分--这就意味着负责他们的创建与销毁
  • 倘若对于部分的职责由其他对象来承担的话,组成也就可以放松这些职责。
  • 如果组成销毁的话,它必须销毁所有的部分,或者把负责他们的权利转移给其他对象。

设计模式所解决的问题:
通过显示指定类创建对象:
     相关的设计模式:简单工厂、工厂方法、抽象工厂。
紧耦合:
    相关的设计模式:抽象工厂、命令模式、外观模式、中介者模式、观察者模式、职责链模式等。
对对象表示或实现的依赖:
     相关的设计模式:抽象工厂、桥接模式、备忘录模式、代理模式等。
通过生成子类扩展功能:
     相关的设计模式:桥接模式、职责链模式、组合模式、装饰模式、观察者模式、策略模式等。
有能方便地修改类:
     相关的设计模式:适配器模式、装饰模式、访问者模式等。
对算法的依赖:
     相关设计模式: 生成器模式、迭代模式、策略模式、模板方法模式、访问者模式等。
对软硬件环境的依赖:
     相关设计模式:抽象工厂模式、桥接模式等。

参考:http://ankye1234.blog.163.com/blog/static/964828920075444512292/







你可能感兴趣的:(C#设计模式前言)