C#常見的設計模式html
1、概要:程序員
模式分爲三種,設計模式、體系結構模式與慣用法。其中慣用法是一種語言緊密相關的模式,例如,定界加鎖模式實際上是一種慣用法。web
在C#項目開發過程當中,不少狀況下您已經使用了某些模式,但或許您並不知道本身所使用的這種解決方案是一種已經被總結概括的模式。正則表達式
工廠、策略、橋接、模板方法、代理等等23種Gof經典模式是屬於設計模式,設計模式的粒度相對較小,基本上用於提升模塊內部的可擴展性和可維護性需求算法
三層、MVC、IoC/DI等屬於體系結構模式,粒度比設計模式大,它是從項目的總體角度去看項目的架構。設計須要合理性,架構一樣追求合理性,這就是架構模式的用途。sql
C#的經典樣例petshop中,採用分層思想構架了整個網站,這裏的分層就是體系結構模式;而在數據庫訪問層,則採用工廠模式來泛化數據庫操做,使得業務層不須要關心您如今的數據庫是SQL server的,仍是oracle的。這就是設計模式的使用。數據庫
模式應用不是一兩句話可以說清楚的,也不是一天兩天可以體會的,須要樓主慢慢體會與學習。編程
2、分類目錄:c#
建立型:windows
1. 單件模式(Singleton Pattern)
2. 抽象工廠(Abstract Factory)
3. 建造者模式(Builder)
4. 工廠方法模式(Factory Method)
5. 原型模式(Prototype)
結構型:
6. 適配器模式(Adapter Pattern)
7. 橋接模式(Bridge Pattern)
8. 裝飾模式(Decorator Pattern)
9. 組合模式(Composite Pattern)
10. 外觀模式(Facade Pattern)
11. 享元模式(Flyweight Pattern)
12. 代理模式(Proxy Pattern)
13. 模板方法(Template Method)
14. 命令模式(Command Pattern)
15. 迭代器模式(Iterator Pattern)
行爲型:
16. 觀察者模式(Observer Pattern)
17. 解釋器模式(Interpreter Pattern)
18. 中介者模式(Mediator Pattern)
19. 職責鏈模式(Chain of Responsibility Pattern)
20. 備忘錄模式(Memento Pattern)
21. 策略模式(Strategy Pattern)
22. 訪問者模式(Visitor Pattern)
23. 狀態模式(State Pattern)
C# 23種設計模式
China Document 4 Colors
How are you
動機(Motivation):
在軟件系統中,常常有這樣一些特殊的類,必須保證它們在系統中只存在一個實例,才能確保它們的邏輯正確性、以及良好的效率。
如何繞過常規的構造器,提供一種機制來保證一個類只建立一個實例?
這應該是類設計者的責任,而不是類使用者的責任。
結構圖:
意圖:
保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。
------<<設計模式>>GOF
生活的例子:
適用性:
(1)當類只能有一個實例並且客戶能夠從一個衆所周知的訪問點訪問它時。
(2)當這個惟一實例應該是經過子類化可擴展的,而且客戶應該無需更改代碼就能使用一個擴展的實例時。
代碼實現:
(1)單線程Singleton實現
class SingleThread_Singleton
{
private static SingleThread_Singleton instance = null;
private SingleThread_Singleton(){}
public static SingleThread_Singleton Instance
{
get
{
if (instance == null)
{
instance = new SingleThread_Singleton();
}
return instance;
}
}
}
以上代碼在單線程狀況下不會出現任何問題。可是在多線程的狀況下卻不是安全的。
如兩個線程同時運行到 if (instance == null)判斷是否被實例化,一個線程判斷爲True後,在進行建立
instance = new SingleThread_Singleton();以前,另外一個線程也判斷(instance == null),結果也爲True.
這樣就就違背了Singleton模式的原則(保證一個類僅有一個實例)。
怎樣在多線程狀況下實現Singleton?
(2)多線程Singleton實現:
1 class MultiThread_Singleton
2 {
3 private static volatile MultiThread_Singleton instance = null;
4 private static object lockHelper = new object();
5 private MultiThread_Singleton() { }
6 public static MultiThread_Singleton Instance
7 {
8 get
9 {
10 if (instance == null)
11 {
12 lock (lockHelper)
13 {
14 if (instance == null)
15 {
16 instance = new MultiThread_Singleton();
17 }
18 }
19 }
20 return instance;
21 }
22 }
23
此程序對多線程是安全的,使用了一個輔助對象lockHelper,保證只有一個線程建立實例(若是instance爲空,保證只有一個線程instance = new MultiThread_Singleton();建立惟一的一個實例)。(Double Check)
請注意一個關鍵字volatile,若是去掉這個關鍵字,仍是有可能發生線程不是安全的。
volatile 保證嚴格意義的多線程編譯器在代碼編譯時對指令不進行微調。
(3)靜態Singleton實現
3 class Static_Singleton
4 {
5 public static readonly Static_Singleton instance = new Static_Singleton();
6 private Static_Singleton() { }
7 }
以上代碼展開等同於
1 class Static_Singleton
2 {
3 public static readonly Static_Singleton instance;
4 static Static_Singleton()
5 {
6 instance = new Static_Singleton();
7 }
8 private Static_Singleton() { }
9 }
由此能夠看出,徹底符合Singleton的原則。
優勢: 簡潔,易懂
缺點: 不能夠實現帶參數實例的建立
常規的對象建立方法:
//建立一個Road對象
Road road =new Road();
new 的問題:
實現依賴,不能應對「具體實例化類型」的變化。
解決思路:
封裝變化點-----哪裏變化,封裝哪裏
潛臺詞: 若是沒有變化,固然不須要額外的封裝!
工廠模式的緣起
變化點在「對象建立」,所以就封裝「對象建立」
面向接口編程----依賴接口,而非依賴實現
最簡單的解決方法:
1 class RoadFactory{
2 public static Road CreateRoad()
3 {
4 return new Road();
5 }
6 }
7 //建立一個Road對象
8 Road road=roadFactory.CreateRoad();
建立一系列相互依賴對象的建立工做:
假設一個遊戲開場景:
咱們須要構造"道路"、"房屋"、"地道","從林"...等等對象
工廠方法以下:
1 class RoadFactory
2 {
3 public static Road CreateRoad()
4 {
5 return new Road();
6 }
7 public static Building CreateBuilding()
8 {
9 return new Building();
10 }
11 public static Tunnel CreateTunnel()
12 {
13 return new Tunnel();
14 }
15 public static Jungle CreateJungle()
16 {
17 return new Jungle();
18 }
19 }
調用方式以下:
1 Road road = RoadFactory.CreateRoad();
3 Building building = RoadFactory.CreateBuilding();
4 Tunnel tunnel = RoadFactory.CreateTunnel();
5 Jungle jungle = RoadFactory.CreateJungle();
如上可見簡單工廠的問題:
不能應對"不一樣系列對象"的變化。好比有不一樣風格的場景---對應不一樣風格的道路,房屋、地道....
如何解決:
使用面嚮對象的技術來"封裝"變化點。
動機(Motivate):
在軟件系統中,常常面臨着"一系統相互依賴的對象"的建立工做:同時,因爲需求的變化,每每存在更多系列對象的建立工做。
如何應對這種變化?如何繞過常規的對象建立方法(new),提供一種"封裝機制"來避免客戶程序和這種"多系列具體對象建立工做"的緊耦合?
意圖(Intent):
提供一個建立一系列相關或相互依賴對象的接口,而無需指定它們具體的類。
----《設計模式》GOF
結構圖(Struct):
適用性:
1.一個系統要獨立於它的產品的建立、組合和表示時。
2.一個系統要由多個產品系統中的一個來配置時。
3.當你要強調一系列相關的產品對象的設計以便進行聯合使用時。
4.當你提供一個產品類庫,而只想顯示它們的接口不是實現時。
生活例子:
結構圖代碼實現:
1 abstract class AbstractFactory
2 {
3 public abstract AbstractProductA CreateProductA();
4 public abstract AbstractProductB CreateProductB();
5 }
1 abstract class AbstractProductA
2 {
3 public abstract void Interact(AbstractProductB b);
4 }
1 abstract class AbstractProductB
2 {
3 public abstract void Interact(AbstractProductA a);
4 }
1 class Client
2 {
3 private AbstractProductA AbstractProductA;
4 private AbstractProductB AbstractProductB;
5 public Client(AbstractFactory factory)
6 {
7 AbstractProductA = factory.CreateProductA();
8 AbstractProductB = factory.CreateProductB();
9 }
10 public void Run()
11 {
12 AbstractProductB.Interact(AbstractProductA);
13 AbstractProductA.Interact(AbstractProductB);
14 }
15 }
1 class ConcreteFactory1:AbstractFactory
2 {
3 public override AbstractProductA CreateProductA()
4 {
5 return new ProductA1();
6 }
7 public override AbstractProductB CreateProductB()
8 {
9 return new ProductB1();
10 }
11 }
1 class ConcreteFactory2:AbstractFactory
2 {
3 public override AbstractProductA CreateProductA()
4 {
5 return new ProdcutA2();
6 }
7 public override AbstractProductB CreateProductB()
8 {
9 return new ProductB2();
10 }
11 }
1 class ProductA1:AbstractProductA
2 {
3 public override void Interact(AbstractProductB b)
4 {
5 Console.WriteLine(this.GetType().Name + "interact with" + b.GetType().Name);
6 }
7 }
1 class ProductB1:AbstractProductB
2 {
3 public override void Interact(AbstractProductA a)
4 {
5 Console.WriteLine(this.GetType().Name + "interact with" + a.GetType().Name);
6 }
7 }
1 class ProdcutA2:AbstractProductA
2 {
3 public override void Interact(AbstractProductB b)
4 {
5 Console.WriteLine(this.GetType().Name + "interact with" + b.GetType().Name);
6 }
7 }
1 class ProductB2:AbstractProductB
2 {
3 public override void Interact(AbstractProductA a)
4 {
5 Console.WriteLine(this.GetType().Name + "interact with" + a.GetType().Name);
6 }
7 }
1 public static void Main()
2 {
3 // Abstractfactory1
4 AbstractFactory factory1 = new ConcreteFactory1();
5 Client c1 = new Client(factory1);
6 c1.Run();
7 // Abstractfactory2
8 AbstractFactory factory2 = new ConcreteFactory2();
9 Client c2 = new Client(factory2);
10 c2.Run();
11 }
Abstract Factory注意的幾點:
若是不存在」多系列對象建立「的需求變化,則不必應用Abstract Factory模式,靜態工廠方法足矣。
"系列對象"指的是這些對象之間有相互依賴、或做用的關係。例如遊戲開發場景中的"道路"與"房屋"依賴,「道路」與「地道」的依賴。
Abstract Factory模式主要在於應對"新系列"的需求變更。其缺點在於難以應對」新對象「的需求變更。
Abstract Factory模式常常和Factory Method模式共同組合來應對「對象建立」的需求變化
Builder模式的緣起:
假設建立遊戲中的一個房屋House設施,該房屋的構建由幾部分組成,且各個部分富於變化。若是使用最直觀的設計方法,每個房屋部分的變化,都將致使房屋構建的從新修正.....
動機(Motivation):
在軟件系統中,有時候面臨一個"複雜對象"的建立工做,其一般由各個部分的子對象用必定算法構成;因爲需求的變化,這個複雜對象的各個部分常常面臨着劇烈的變化,可是將它們組合到一塊兒的算法卻相對穩定。
如何應對種變化呢?如何提供一種"封裝機制"來隔離出"複雜對象的各個部分"的變化,從而保持系統中的"穩定構建算法"不隨需求的改變而改變?
意圖(Intent):
將一個複雜對象的構建與其表示相分離,使得一樣的構建過程能夠建立不一樣的表示。
-------《設計模式》GOF
結構圖(Struct):
協做(Collaborations):
生活中的例子:
適用性:
1.當建立複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
2.當構造過程必須容許被構造的對象有不一樣的表示時。
實例代碼:
Builder類:
1 public abstract class Builder
2 {
3 public abstract void BuildDoor();
4 public abstract void BuildWall();
5 public abstract void BuildWindows();
6 public abstract void BuildFloor();
7 public abstract void BuildHouseCeiling();
8
9 public abstract House GetHouse();
10 }
Director類:這一部分是 組合到一塊兒的算法(相對穩定)。
1 public class Director
2 {
3 public void Construct(Builder builder)
4 {
5 builder.BuildWall();
6 builder.BuildHouseCeiling();
7 builder.BuildDoor();
8 builder.BuildWindows();
9 builder.BuildFloor();
10 }
11 }
ChineseBuilder類
1 public class ChineseBuilder:Builder
2 {
3 private House ChineseHouse = new House();
4 public override void BuildDoor()
5 {
6 Console.WriteLine("this Door 's style of Chinese");
7 }
8 public override void BuildWall()
9 {
10 Console.WriteLine("this Wall 's style of Chinese");
11 }
12 public override void BuildWindows()
13 {
14 Console.WriteLine("this Windows 's style of Chinese");
15 }
16 public override void BuildFloor()
17 {
18 Console.WriteLine("this Floor 's style of Chinese");
19 }
20 public override void BuildHouseCeiling()
21 {
22 Console.WriteLine("this Ceiling 's style of Chinese");
23 }
24 public override House GetHouse()
25 {
26 return ChineseHouse;
27 }
28 }
RomanBuilder類:
1 class RomanBuilder:Builder
2 {
3 private House RomanHouse = new House();
4 public override void BuildDoor()
5 {
6 Console.WriteLine("this Door 's style of Roman");
7 }
8 public override void BuildWall()
9 {
10 Console.WriteLine("this Wall 's style of Roman");
11 }
12 public override void BuildWindows()
13 {
14 Console.WriteLine("this Windows 's style of Roman");
15 }
16 public override void BuildFloor()
17 {
18 Console.WriteLine("this Floor 's style of Roman");
19 }
20 public override void BuildHouseCeiling()
21 {
22 Console.WriteLine("this Ceiling 's style of Roman");
23 }
24 public override House GetHouse()
25 {
26 return RomanHouse;
27 }
28 }
ChineseBuilder和RomanBuilder這兩個是:這個複雜對象的兩個部分常常面臨着劇烈的變化。
1 public class Client
2 {
3 public static void Main(string[] args)
4 {
5 Director director = new Director();
6
7 Builder instance;
8
9 Console.WriteLine("Please Enter House No:");
10
11 string No = Console.ReadLine();
12
13 string houseType = ConfigurationSettings.AppSettings["No" + No];
14
15 instance = (Builder)Assembly.Load("House").CreateInstance("House." + houseType);
16
17 director.Construct(instance);
18
19 House house= instance.GetHouse();
20 house.Show();
21
22 Console.ReadLine();
23 }
24 }
1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <appSettings>
4 <add key="No1" value="RomanBuilder"></add>
5 <add key="No2" value="ChineseBuilder"></add>
6 </appSettings>
7 </configuration>
Builder模式的幾個要點:
Builder模式 主要用於「分步驟構建一個複雜的對象」。在這其中「分步驟」是一個穩定的乘法,而複雜對象的各個部分則常常變化。
Builder模式主要在於應對「複雜對象各個部分」的頻繁需求變更。其缺點在於難以應對「分步驟構建算法」的需求變更。
Abstract Factory模式解決「系列對象」的需求變化,Builder模式解決「對象部分」的需求變化。Builder械一般和Composite模式組合使用
耦合關係:
動機(Motivation):
在軟件系統中,因爲需求的變化,"這個對象的具體實現"常常面臨着劇烈的變化,但它卻有比較穩定的接口。
如何應對這種變化呢?提供一種封裝機制來隔離出"這個易變對象"的變化,從而保持系統中"其它依賴的對象"不隨需求的變化而變化。
意圖(Intent):
定義一個用戶建立對象的接口,讓子類決定實例哪個類。Factory Method使一個類的實例化延遲到子類。
----------《設計模式》GOF
結構圖(Struct):
生活實例:
適用性:
1.當一個類不知道它所必須建立的對象類的時候。
2.當一個類但願由它子類來指定它所建立對象的時候。
3.當類將建立對象的職責委託給多個幫助子類中的某個,而且你但願將哪個幫助子類是代理者這一信息局部化的時候。
實例代碼:
CarFactory類:
1 public abstract class CarFactory
2 {
3 public abstract Car CarCreate();
4 }
Car類:
1 public abstract class Car
2 {
3 public abstract void StartUp();
4 public abstract void Run();
5 public abstract void Stop();
6
7 }
HongQiCarFactory類:
1 public class HongQiCarFactory:CarFactory
2 {
3 public override Car CarCreate()
4 {
5 return new HongQiCar();
6 }
7 }
BMWCarFactory類:
1 public class BMWCarFactory:CarFactory
2 {
3 public override Car CarCreate()
4 {
5 return new BMWCar();
6 }
7 }
HongQiCar類:
1 public class HongQiCar:Car
2 {
3 public override void StartUp()
4 {
5 Console.WriteLine("Test HongQiCar start-up speed!");
6 }
7 public override void Run()
8 {
9 Console.WriteLine("The HongQiCar run is very quickly!");
10 }
11 public override void Stop()
12 {
13 Console.WriteLine("The slow stop time is 3 second ");
14 }
15 }
BMWCar類:
1 public class BMWCar:Car
2 {
3 public override void StartUp()
4 {
5 Console.WriteLine("The BMWCar start-up speed is very quickly");
6 }
7 public override void Run()
8 {
9 Console.WriteLine("The BMWCar run is quitely fast and safe!!!");
10 }
11 public override void Stop()
12 {
13 Console.WriteLine("The slow stop time is 2 second");
14 }
15 }
app.config
1 <?xml version="1.0" encoding="utf-8" ?>
2 <configuration>
3 <appSettings>
4 <add key="No1" value="HongQiCarFactory"/>
5 <add key="No2" value="BMWCarFactory"/>
6 </appSettings>
7 </configuration>
Program類:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Console.WriteLine("Please Enter Factory Method No:");
6 Console.WriteLine("******************************");
7 Console.WriteLine("no Factory Method");
8 Console.WriteLine("1 HongQiCarFactory");
9 Console.WriteLine("2 BMWCarFactory");
10 Console.WriteLine("******************************");
11 int no=Int32.Parse(Console.ReadLine().ToString());
12 string factoryType=ConfigurationManager.AppSettings["No"+no];
13 //CarFactory factory = new HongQiCarFactory();
14 CarFactory factory = (CarFactory)Assembly.Load("FactoryMehtod").CreateInstance("FactoryMehtod." + factoryType); ;
15 Car car=factory.CarCreate();
16 car.StartUp();
17 car.Run();
18 car.Stop();
19
20 }
21 }
Factory Method 模式的幾個要點:
Factory Method模式主要用於隔離類對象的使用者和具體類型之間的耦合關係。面對一個常常變化的具體類型,緊耦合關係會致使軟件的脆弱。
Factory Method模式經過面向對象的手法,將所要建立的具體對象工做延遲到子類,從而實現一種擴展(而非更改)的策略,較好地解決了這種緊耦合關係。
Factory Mehtod模式解決"單個對象"的需求變化,AbstractFactory模式解決"系列對象"的需求變化,Builder模式解決"對象部分"的需求變化
依賴關係倒置:
動機(Motivate):
在軟件系統中,常常面臨着「某些結構複雜的對象」的建立工做;因爲需求的變化,這些對象常常面臨着
劇烈的變化,可是它們卻擁有比較穩定一致的接口。
如何應對這種變化?如何向「客戶程序(使用這些對象的程序)"隔離出「這些易變對象」,從而使得「依賴這些易變對象的客戶程序」不隨着需求改變而改變?
意圖(Intent):
用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。
------《設計模式》GOF
結構圖(Struct):
生活例子:
適用性:
1.當一個系統應該獨立於它的產品建立,構成和表示時;
2.當要實例化的類是在運行時刻指定時,例如,經過動態裝載;
3.爲了不建立一個與產品類層次平行的工廠類層次時;
4.當一個類的實例只能有幾個不一樣狀態組合中的一種時。創建相應數目的原型並克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。
示意性代碼例子:
1 public abstract class NormalActor
2 {
3 public abstract NormalActor clone();
4 }
1 public class NormalActorA:NormalActor
2 {
3 public override NormalActor clone()
4 {
5 Console.WriteLine("NormalActorA is call");
6 return (NormalActor)this.MemberwiseClone();
7
8 }
9 }
1 public class NormalActorB :NormalActor
2 {
3 public override NormalActor clone()
4 {
5 Console.WriteLine("NormalActorB was called");
6 return (NormalActor)this.MemberwiseClone();
7
8 }
9 }
public class GameSystem
{
public void Run(NormalActor normalActor)
{
NormalActor normalActor1 = normalActor.clone();
NormalActor normalActor2 = normalActor.clone();
NormalActor normalActor3 = normalActor.clone();
NormalActor normalActor4 = normalActor.clone();
NormalActor normalActor5 = normalActor.clone();
}
}
class Program
{
static void Main(string[] args)
{
GameSystem gameSystem = new GameSystem();
gameSystem.Run(new NormalActorA());
}
}
若是又須要建立新的對象(flyActor),只需建立此抽象類,而後具體類進行克隆。
public abstract class FlyActor
{
public abstract FlyActor clone();
}
public class FlyActorB:FlyActor
{
/// <summary>
/// 淺拷貝,若是用深拷貝,可以使用序列化
/// </summary>
/// <returns></returns>
public override FlyActor clone()
{
return (FlyActor)this.MemberwiseClone();
}
}
此時,調用的Main()函數只需以下:
class Program
{
static void Main(string[] args)
{
GameSystem gameSystem = new GameSystem();
gameSystem.Run(new NormalActorA(), new FlyActorB());
}
}
Prototype的幾個要點:
Prototype模式一樣用於隔離類對象的使用者和具體類型(易變類)之間的耦合關係,它一樣要求這
些「易變類」擁有「穩定的接口」。
Prototype模式對於「如何建立易變類的實體對象「採用「原型克隆」的方法來作,它使得咱們能夠
很是靈活地動態建立「擁有某些穩定接口中」的新對象----所需工做僅僅是註冊的地方不斷地Clone.
Prototype模式中的Clone方法能夠利用.net中的object類的memberwiseClone()方法或者序列化來實現深拷貝。
有關建立型模式的討論:
Singleton模式解決的是實體對象個數的問題。除了Singleton以外,其餘建立型模式解決的是都是new 所帶來的耦合關係。
Factory Method ,Abstract Factory,Builder都須要一個額外的工廠類來負責實例化「易變對象」,而Prototype則是經過原型(一個特殊的工廠類)來克隆「易變對象」。
若是遇到「易變類」,起初的設計一般從Factory Mehtod開始,當遇到更多的複雜變化時,再考慮重重構爲其餘三種工廠模式(Abstract Factory,Builder,Prototype)
適配(轉換)的概念無處不在......
適配,即在不改變原有實現的基礎上,將原先不兼容的接口轉換爲兼容的接口。
例如:二轉換爲三箱插頭,將高電壓轉換爲低電壓等。
動機(Motivate):
在軟件系統中,因爲應用環境的變化,經常須要將「一些現存的對象」放在新的環境中應用,可是新環境要求的接口是這些現存對象所不知足的。
那麼如何應對這種「遷移的變化」?如何既能利用現有對象的良好實現,同時又能知足新的應用環境所要求的接口?這就是本文要說的Adapter 模式。
意圖(Intent):
將一個類的接口轉換成客戶但願的另一個接口。Adapter模式使得本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。
-------《設計模式》GOF
結構(Struct):
圖1:對象適配器
圖2:類適配器
生活中的例子:
適用性:
1.系統須要使用現有的類,而此類的接口不符合系統的須要。
2.想要創建一個能夠重複使用的類,用於與一些彼此之間沒有太大關聯的一些類,包括一些可能在未來引進的類一塊兒工做。這些源類不必定有很複雜的接口。
3.(對對象適配器而言)在設計裏,須要改變多個已有子類的接口,若是使用類的適配器模式,就要針對每個子類作一個適配器,而這不太實際。
示意性代碼實例:
1 interface IStack
2 {
3 void Push(object item);
4 void Pop();
5 object Peek();
6 }
1 //對象適配器(Adapter與Adaptee組合的關係)
2 public class Adapter : IStack //適配對象
3 {
4 ArrayList adaptee;//被適配的對象
5 public Adapter()
6 {
7 adaptee = new ArrayList();
8 }
9 public void Push(object item)
10 {
11 adaptee.Add(item);
12 }
13 public void Pop()
14 {
15 adaptee.RemoveAt(adaptee.Count - 1);
16 }
17 public object Peek()
18 {
19 return adaptee[adaptee.Count - 1];
20 }
21 }
類適配器
1 public class Adapter :ArrayList, IStack
2 {
3 public void Push(object item)
4 {
5 this.Add(item);
6 }
7 public void Pop()
8 {
9 this.RemoveAt(this.Count - 1);
10 }
11 public object Peek()
12 {
13 return this[this.Count - 1];
14 }
15 }
Adapter模式的幾個要點:
Adapter模式主要應用於「但願複用一些現存的類,可是接口又與複用環境要求不一致的狀況」,在遺留代碼複用、類庫遷移等方面很是有用。
GOF23定義了兩種Adapter模式的實現結構:對象適配器和類適配器。但類適配器採用「多繼承」的實現方式,帶來不良的高耦合,因此通常不推薦使用。對象適配器採用「對象組合」的方式,更符合鬆耦合精神。
Adapter模式能夠實現的很是靈活,沒必要拘泥於GOF23中定義的兩種結構。例如,徹底能夠將Adapter模式中的「現存對象「做爲新的接口方法參數,來達到適配的目的。
Adapter模式自己要求咱們儘量地使用」面向接口的編程"風格,這樣才能在後期很方便的適配。
.NET框架中的Adapter應用:
(1)在.Net中複用com對象:
Com 對象不符合.net對象的接口
使用tlbimp.exe來建立一個Runtime Callable Wrapper(RCW)以使其符合.net對象的接口。
(2).NET數據訪問類(Adapter變體):
各類數據庫並無提供DataSet接口
使用DBDataAdapter能夠將任何各數據庫訪問/存取適配到一個DataSet對象上。
(3)集合類中對現有對象的排序(Adapter變體);
現有對象未實現IComparable接口
實現一個排序適配器(繼承IComparer接口),而後在其Compare方法中對兩個對象進行比較。
動機(Motivate):
在軟件系統中,某些類型因爲自身的邏輯,它具備兩個或多個維度的變化,那麼如何應對這種「多維度的變化」?如何利用面嚮對象的技術來使得該類型可以輕鬆的沿着多個方向進行變化,而又不引入額外的複雜度?
意圖(Intent):
將抽象部分與實現部分分離,使它們均可以獨立的變化。
------《設計模式》GOF
結構圖(Struct):
生活中的例子:
我想你們小時候都有用蠟筆畫畫的經歷吧。紅紅綠綠的蠟筆一大盒,根據想象描繪出格式圖樣。而毛筆下的國畫更是工筆寫意,各展風采。而今天咱們的故事從蠟筆與毛筆提及。
設想要繪製一幅圖畫,藍天、白雲、綠樹、小鳥,若是畫面尺寸很大,那麼用蠟筆繪製就會遇到點麻煩。畢竟細細的蠟筆要塗出一片藍天,是有些麻煩。若是有可能,最好有套大號蠟筆,粗粗的蠟筆很快能塗抹完成。至於色彩嗎,最好每種顏色來支粗的,除了藍天還有綠地呢。這樣,若是一套12種顏色的蠟筆,咱們須要兩套 24支,同種顏色的一粗一細。呵呵,畫還沒畫,開始作夢了:要是再有一套中號蠟筆就更好了,這樣,很少很多總共36支蠟筆。
再看看毛筆這一邊,竟然如此簡陋:一套水彩12色,外加大中小三支毛筆。你可別小瞧這"簡陋"的組合,畫藍天用大毛筆,畫小鳥用小毛筆,各具特點。
呵呵,您是否是已經看出來了,不錯,我今天要說的就是Bridge模式。爲了一幅畫,咱們須要準備36支型號不一樣的蠟筆,而改用毛筆三支就夠了,固然還要搭配上12種顏料。經過Bridge模式,咱們把乘法運算3×12=36改成了加法運算3+12=15,這一改進可不小。那麼咱們這裏蠟筆和毛筆到底有什麼區別呢?
實際上,蠟筆和毛筆的關鍵一個區別就在於筆和顏色是否可以分離。【GOF95】橋樑模式的用意是"將抽象化 (Abstraction)與實現化(Implementation)脫耦,使得兩者能夠獨立地變化"。關鍵就在於可否脫耦。蠟筆的顏色和蠟筆自己是分不開的,因此就形成必須使用36支色彩、大小各異的蠟筆來繪製圖畫。而毛筆與顏料可以很好的脫耦,各自獨立變化,便簡化了操做。在這裏,抽象層面的概念是: "毛筆用顏料做畫",而在實現時,毛筆有大中小三號,顏料有紅綠藍等12種,因而即可出現3×12種組合。每一個參與者(毛筆與顏料)均可以在本身的自由度上隨意轉換。
蠟筆因爲沒法將筆與顏色分離,形成筆與顏色兩個自由度沒法單獨變化,使得只有建立36種對象才能完成任務。Bridge模式將繼承關係轉換爲組合關係,從而下降了系統間的耦合,減小了代碼編寫量。
代碼實現:
1 abstract class Brush
2 {
3 protected Color c;
4 public abstract void Paint();
5
6 public void SetColor(Color c)
7 { this.c = c; }
8 }
1 class BigBrush : Brush
2 {
3 public override void Paint()
4 { Console.WriteLine("Using big brush and color {0} painting", c.color); }
5 }
1 class SmallBrush : Brush
2 {
3 public override void Paint()
4 { Console.WriteLine("Using small brush and color {0} painting", c.color); }
5 }
1 class Color
2 {
3 public string color;
4 }
1 class Red : Color
2 {
3 public Red()
4 { this.color = "red"; }
5 }
1 class Green : Color
2 {
3 public Green()
4 { this.color = "green"; }
5 }
1 class Blue : Color
2 {
3 public Blue()
4 { this.color = "blue"; }
5 }
1 class Program
2 {
3 public static void Main()
4 {
5 Brush b = new BigBrush();
6 b.SetColor(new Red());
7 b.Paint();
8 b.SetColor(new Blue());
9 b.Paint();
10 b.SetColor(new Green());
11 b.Paint();
12
13 b = new SmallBrush();
14 b.SetColor(new Red());
15 b.Paint();
16 b.SetColor(new Blue());
17 b.Paint();
18 b.SetColor(new Green());
19 b.Paint();
20 }
適用性:
1.若是一個系統須要在構件的抽象化角色和具體化角色之間增長更多的靈活性,避免在兩個層次之間創建靜態的聯繫。
2.設計要求實現化角色的任何改變不該當影響客戶端,或者說實現化角色的改變對客戶端是徹底透明的。
3 .一個構件有多於一個的抽象化角色和實現化角色,系統須要它們之間進行動態耦合。
4 .雖然在系統中使用繼承是沒有問題的,可是因爲抽象化角色和具體化角色須要獨立變化,設計要求須要獨立管理這二者。
Bridge要點:
1.Bridge模式使用「對象間的組合關係」解耦了抽象和實現之間固有的綁定關係,使得抽象和實現能夠沿着各自的維度來變化。
2.所謂抽象和實現沿着各自維度的變化,即「子類化」它們,獲得各個子類以後,即可以任意它們,從而得到不一樣平臺上的不一樣型號。
3.Bridge模式有時候相似於多繼承方案,可是多繼承方案每每違背了類的單一職責原則(即一個類只有一個變化的緣由),複用性比較差。Bridge模式是比多繼承方案更好的解決方法。
4.Bridge模式的應用通常在「兩個很是強的變化維度」,有時候即便有兩個變化的維度,可是某個方向的變化維度並不劇烈——換言之兩個變化不會致使縱橫交錯的結果,並不必定要使用Bridge模式。
子類復子類,子類何其多
假如咱們須要爲遊戲中開發一種坦克,除了各類不一樣型號的坦克外,咱們還但願在不一樣場合中爲其增長如下一種或多種功能;好比紅外線夜視功能,好比水陸兩棲功能,好比衛星定位功能等等。
按類繼承的做法以下:
1 //抽象坦克
2 public abstract class Tank
3 {
4 public abstract void Shot();
5 public abstract void Run();
6 }
各類型號:
1 //T50型號
2 public class T50:Tank
3 {
4 public override void Shot()
5 {
6 Console.WriteLine("T50坦克平均每秒射擊5發子彈");
7 }
8 public override void Run()
9 {
10 Console.WriteLine("T50坦克平均每時運行30千米");
11 }
12 }
1 //T75型號
2 public class T75 : Tank
3 {
4 public override void Shot()
5 {
6 Console.WriteLine("T75坦克平均每秒射擊10發子彈");
7 }
8 public override void Run()
9 {
10 Console.WriteLine("T75坦克平均每時運行35千米");
11 }
12 }
1 //T90型號
2 public class T90 :Tank
3 {
4 public override void Shot()
5 {
6 Console.WriteLine("T90坦克平均每秒射擊10發子彈");
7 }
8 public override void Run()
9 {
10 Console.WriteLine("T90坦克平均每時運行40千米");
11 }
12 }
各類不一樣功能的組合:好比IA具備紅外功能接口、IB具備水陸兩棲功能接口、IC具備衛星定位功能接口。
1 //T50坦克各類功能的組合
2 public class T50A:T50,IA
3 {
4 //具備紅外功能
5 }
6 public class T50B:T50,IB
7 {
8 //具備水陸兩棲功能
9 }
10 public class T50C:T50,IC
11 {
12
13 }
14 public class T50AB:T50,IA,IB
15 {}
18 public class T50AC:T50,IA,IC
19 {}
20 public class T50BC:T50,IB,IC
21 {}
22 public class T50ABC:T50,IA,IB,IC
23 {}
1
2 //T75各類不一樣型號坦克各類功能的組合
3 public class T75A:T75,IA
4 {
5 //具備紅外功能
6 }
7 public class T75B:T75,IB
8 {
9 //具備水陸兩棲功能
10 }
11 public class T75C:T75,IC
12 {
13 //具備衛星定位功能
14 }
15 public class T75AB:T75,IA,IB
16 {
17 //具備紅外、水陸兩棲功能
18 }
19 public class T75AC:T75,IA,IC
20 {
21 //具備紅外、衛星定位功能
22 }
23 public class T75BC:T75,IB,IC
24 {
25 //具備水陸兩棲、衛星定位功能
26 }
27 public class T75ABC:T75,IA,IB,IC
28 {
29 //具備紅外、水陸兩棲、衛星定位功能
30 }
1
2 //T90各類不一樣型號坦克各類功能的組合
3 public class T90A:T90,IA
4 {
5 //具備紅外功能
6 }
7 public class T90B:T90,IB
8 {
9 //具備水陸兩棲功能
10 }
11 public class T90C:T90,IC
12 {
13 //具備衛星定位功能
14 }
15 public class T90AB:T90,IA,IB
16 {
17 //具備紅外、水陸兩棲功能
18 }
19 public class T90AC:T90,IA,IC
20 {
21 //具備紅外、衛星定位功能
22 }
23 public class T90BC:T90,IB,IC
24 {
25 //具備水陸兩棲、衛星定位功能
26 }
27 public class T90ABC:T90,IA,IB,IC
28 {
29 //具備紅外、水陸兩棲、衛星定位功能
30 }
因而可知,若是用類繼承實現,子類會爆炸式地增加。
動機(Motivate):
上述描述的問題根源在於咱們「過分地使用了繼承來擴展對象的功能」,因爲繼承爲類型引入的靜態物質,使得這種擴展方式缺少靈活性;而且隨着子類的增多(擴展功能的增多),各類子類的組合(擴展功能組合)會致使更多子類的膨脹(多繼承)。
如何使「對象功能的擴展」可以根據須要來動態地實現?同時避免「擴展功能的增多」帶來的子類膨脹問題?從而使得任何「功能擴展變化」所致使的影響將爲最低?
意圖(Intent):
動態地給一個對象添加一些額外的職責。就增長功能來講,Decorator模式相比生成子類更爲靈活。
------《設計模式》GOF
結構圖(Struct):
生活中的例子:
適用性:
須要擴展一個類的功能,或給一個類增長附加責任。
須要動態地給一個對象增長功能,這些功能能夠再動態地撤銷。
須要增長由一些基本功能的排列組合而產生的很是大量的功能,從而使繼承關係變得不現實。
實現代碼:
1 namespace Decorator
2 {
3 public abstract class Tank
4 {
5 public abstract void Shot();
6 public abstract void Run();
7 }
8 }
1 namespace Decorator
2 {
3 public class T50:Tank
4 {
5 public override void Shot()
6 {
7 Console.WriteLine("T50坦克平均每秒射擊5發子彈");
8 }
9 public override void Run()
10 {
11 Console.WriteLine("T50坦克平均每時運行30千米");
12 }
13 }
14 }
1 namespace Decorator
2 {
3 public class T75 : Tank
4 {
5 public override void Shot()
6 {
7 Console.WriteLine("T75坦克平均每秒射擊10發子彈");
8 }
9 public override void Run()
10 {
11 Console.WriteLine("T75坦克平均每時運行35千米");
12 }
13 }
14 }
1 namespace Decorator
2 {
3 public class T90 :Tank
4 {
5 public override void Shot()
6 {
7 Console.WriteLine("T90坦克平均每秒射擊10發子彈");
8 }
9 public override void Run()
10 {
11 Console.WriteLine("T90坦克平均每時運行40千米");
12 }
13 }
14 }
1 namespace Decorator
2 {
3 public abstract class Decorator :Tank //Do As 接口繼承 非實現繼承
4 {
5 private Tank tank; //Has a 對象組合
6 public Decorator(Tank tank)
7 {
8 this.tank = tank;
9 }
10 public override void Shot()
11 {
12 tank.Shot();
13 }
14 public override void Run()
15 {
16 tank.Run();
17 }
18 }
19 }
20
1
2 namespace Decorator
3 {
4 public class DecoratorA :Decorator
5 {
6 public DecoratorA(Tank tank) : base(tank)
7 {
8 }
9 public override void Shot()
10 {
11 //Do some extension //功能擴展 且有紅外功能
12 base.Shot();
13 }
14 public override void Run()
15 {
16
17 base.Run();
18 }
19 }
20 }
1 namespace Decorator
2 {
3 public class DecoratorB :Decorator
4 {
5 public DecoratorB(Tank tank) : base(tank)
6 {
7 }
8 public override void Shot()
9 {
10 //Do some extension //功能擴展 且有水陸兩棲功能
11 base.Shot();
12 }
13 public override void Run()
14 {
15
16 base.Run();
17 }
18 }
19 }
20
1 namespace Decorator
2 {
3 public class DecoratorC :Decorator
4 {
5 public DecoratorC(Tank tank) : base(tank)
6 {
7 }
8 public override void Shot()
9 {
10 //Do some extension //功能擴展 且有衛星定位功能
11 base.Shot();
12 }
13 public override void Run()
14 {
15
16 base.Run();
17 }
18
19 }
20 }
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Tank tank = new T50();
6 DecoratorA da = new DecoratorA(tank); //且有紅外功能
7 DecoratorB db = new DecoratorB(da); //且有紅外和水陸兩棲功能
8 DecoratorC dc = new DecoratorC(db); //且有紅外、水陸兩棲、衛星定們三種功能
9 dc.Shot();
10 dc.Run();
11 }
12 }
Decorator模式的幾個要點:
經過採用組合、而非繼承的手法,Decorator模式實現了在運行時動態地擴展對象功能的能力,並且能夠
根據須要擴展多個功能。避免了單獨使用繼承帶來的「靈活性差"和"多子類衍生問題"。
Component類在Decorator模式中充當抽象接口的角色,不該該去實現具體的行爲。並且Decorator類對於Component類應該透明---換言之Component類無需知道Decorator類,Decorator類是從外部來擴展Component類的功能。
Decorator類在接口上表現爲is-a Component的繼承關係,即Decorator類繼承了Component類所且有的接口。但在實現上又表現has a Component的組合關係,即Decorator類又使用了另一個Component類。咱們能夠使用一個或者多個Decorator對象來「裝飾」一個Component對象,且裝飾後的對象仍然是一個Component對象。
Decorator模式並不是解決」多子類衍生的多繼承「問題,Decorator模式應用的要點在於解決「主體
類在多個方向上的擴展功能」------是爲「裝飾」的含義。
Decorator在.NET(Stream)中的應用:
能夠看到, BufferedStream和CryptoStream其實就是兩個包裝類,這裏的Decorator模式省略了抽象裝飾角色(Decorator),示例代碼以下:
1 class Program
2
3 {
4
5 public static void Main(string[] args)
6
7 {
8
9 MemoryStream ms =
10
11 new MemoryStream(new byte[] { 100,456,864,222,567});
12
13
14
15 //擴展了緩衝的功能
16
17 BufferedStream buff = new BufferedStream(ms);
18
19
20
21 //擴展了緩衝,加密的功能
22
23 CryptoStream crypto = new CryptoStream(buff);
24
25 }
26
27 }
經過反編譯,能夠看到BufferedStream類的代碼(只列出部分),它是繼承於Stream類:
1 public sealed class BufferedStream : Stream
2
3 {
4
5 // Methods
6
7 private BufferedStream();
8
9 public BufferedStream(Stream stream);
10
11 public BufferedStream(Stream stream, int bufferSize);
12
13 // Fields
14
15 private int _bufferSize;
16
17 private Stream _s;
18
19 }
動機(Motivate):
組合模式有時候又叫作部分-總體模式,它使咱們樹型結構的問題中,模糊了簡單元素和複雜元素的概念,客戶程序能夠向處理簡單元素同樣來處理複雜元素,從而使得客戶程序與複雜元素的內部結構解耦。
意圖(Intent):
將對象組合成樹形結構以表示「部分-總體」的層次結構。Composite模式使得用戶對單個對象和組合對象的使用具備一致性。
-----------《設計模式》GOF
結構圖(Struct):
生活中的例子:
適用性:
1.你想表示對象的部分-總體層次結構
2.你但願用戶忽略組合對象與單個對象的不一樣,用戶將統一地使用組合結構中的全部對象。
代碼實現:
這裏咱們用繪圖這個例子來講明Composite模式,經過一些基本圖像元素(直線、圓等)以及一些複合圖像元素(由基本圖像元素組合而成)構建複雜的圖形樹。在設計中咱們對每個對象都配備一個Draw()方法,在調用時,會顯示相關的圖形。能夠看到,這裏複合圖像元素它在充當對象的同時,又是那些基本圖像元素的一個容器。先看一下基本的類結構圖:
圖中橙色的區域表示的是複合圖像元素。
示意性代碼:
1 public abstract class Graphics
2 {
3 protected string _name;
4
5 public Graphics(string name)
6 {
7 this._name = name;
8 }
9 public abstract void Draw();
10 }
11
12 public class Picture : Graphics
13 {
14 public Picture(string name)
15 : base(name)
16 { }
17 public override void Draw()
18 {
19 //
20 }
21
22 public ArrayList GetChilds()
23 {
24 //返回全部的子對象
25 }
26 }
而其餘做爲樹枝構件,實現代碼以下:
1 public class Line:Graphics
2 {
3 public Line(string name)
4 : base(name)
5 { }
6
7 public override void Draw()
8 {
9 Console.WriteLine("Draw a" + _name.ToString());
10 }
11 }
12
13 public class Circle : Graphics
14 {
15 public Circle(string name)
16 : base(name)
17 { }
18
19 public override void Draw()
20 {
21 Console.WriteLine("Draw a" + _name.ToString());
22 }
23 }
24
25 public class Rectangle : Graphics
26 {
27 public Rectangle(string name)
28 : base(name)
29 { }
30
31 public override void Draw()
32 {
33 Console.WriteLine("Draw a" + _name.ToString());
34 }
35 }
如今咱們要對該圖像元素進行處理:在客戶端程序中,須要判斷返回對象的具體類型究竟是基本圖像元素,仍是複合圖像元素。若是是複合圖像元素,咱們將要用遞歸去處理,然而這種處理的結果卻增長了客戶端程序與複雜圖像元素內部結構之間的依賴,那麼咱們如何去解耦這種關係呢?咱們但願的是客戶程序能夠像處理基本圖像元素同樣來處理複合圖像元素,這就要引入Composite模式了,須要把對於子對象的管理工做交給複合圖像元素,爲了進行子對象的管理,它必須提供必要的Add(),Remove()等方法,類結構圖以下:
示意代碼:
1 public abstract class Graphics
2 {
3 protected string _name;
4
5 public Graphics(string name)
6 {
7 this._name = name;
8 }
9 public abstract void Draw();
10 public abstract void Add();
11 public abstract void Remove();
12 }
13
14 public class Picture : Graphics
15 {
16 protected ArrayList picList = new ArrayList();
17
18 public Picture(string name)
19 : base(name)
20 { }
21 public override void Draw()
22 {
23 Console.WriteLine("Draw a" + _name.ToString());
24
25 foreach (Graphics g in picList)
26 {
27 g.Draw();
28 }
29 }
30
31 public override void Add(Graphics g)
32 {
33 picList.Add(g);
34 }
35 public override void Remove(Graphics g)
36 {
37 picList.Remove(g);
38 }
39 }
40
41 public class Line : Graphics
42 {
43 public Line(string name)
44 : base(name)
45 { }
46
47 public override void Draw()
48 {
49 Console.WriteLine("Draw a" + _name.ToString());
50 }
51 public override void Add(Graphics g)
52 { }
53 public override void Remove(Graphics g)
54 { }
55 }
56
57 public class Circle : Graphics
58 {
59 public Circle(string name)
60 : base(name)
61 { }
62
63 public override void Draw()
64 {
65 Console.WriteLine("Draw a" + _name.ToString());
66 }
67 public override void Add(Graphics g)
68 { }
69 public override void Remove(Graphics g)
70 { }
71 }
72
73 public class Rectangle : Graphics
74 {
75 public Rectangle(string name)
76 : base(name)
77 { }
78
79 public override void Draw()
80 {
81 Console.WriteLine("Draw a" + _name.ToString());
82 }
83 public override void Add(Graphics g)
84 { }
85 public override void Remove(Graphics g)
86 { }
87 }
這樣引入Composite模式後,客戶端程序再也不依賴於複合圖像元素的內部實現了。然而,咱們程序中仍然存在着問題,由於Line,Rectangle,Circle已經沒有了子對象,它是一個基本圖像元素,所以Add(),Remove()的方法對於它來講沒有任何意義,並且把這種錯誤不會在編譯的時候報錯,把錯誤放在了運行期,咱們但願可以捕獲到這類錯誤,並加以處理,稍微改進一下咱們的程序:
1 public class Line : Graphics
2 {
3 public Line(string name)
4 : base(name)
5 { }
6
7 public override void Draw()
8 {
9 Console.WriteLine("Draw a" + _name.ToString());
10 }
11 public override void Add(Graphics g)
12 {
13 //拋出一個咱們自定義的異常
14 }
15 public override void Remove(Graphics g)
16 {
17 //拋出一個咱們自定義的異常
18 }
19 }
這樣改進之後,咱們能夠捕獲可能出現的錯誤,作進一步的處理。上面的這種實現方法屬於透明式的Composite模式,若是咱們想要更安全的一種作法,就須要把管理子對象的方法聲明在樹枝構件Picture類裏面,這樣若是葉子節點Line,Rectangle,Circle使用這些方法時,在編譯期就會出錯,看一下類結構圖:
示意代碼:
1 public abstract class Graphics
2 {
3 protected string _name;
4
5 public Graphics(string name)
6 {
7 this._name = name;
8 }
9 public abstract void Draw();
10 }
11
12 public class Picture : Graphics
13 {
14 protected ArrayList picList = new ArrayList();
15
16 public Picture(string name)
17 : base(name)
18 { }
19 public override void Draw()
20 {
21 Console.WriteLine("Draw a" + _name.ToString());
22
23 foreach (Graphics g in picList)
24 {
25 g.Draw();
26 }
27 }
28
29 public void Add(Graphics g)
30 {
31 picList.Add(g);
32 }
33 public void Remove(Graphics g)
34 {
35 picList.Remove(g);
36 }
37 }
38
39 public class Line : Graphics
40 {
41 public Line(string name)
42 : base(name)
43 { }
44
45 public override void Draw()
46 {
47 Console.WriteLine("Draw a" + _name.ToString());
48 }
49 }
50
51 public class Circle : Graphics
52 {
53 public Circle(string name)
54 : base(name)
55 { }
56
57 public override void Draw()
58 {
59 Console.WriteLine("Draw a" + _name.ToString());
60 }
61 }
62
63 public class Rectangle : Graphics
64 {
65 public Rectangle(string name)
66 : base(name)
67 { }
68
69 public override void Draw()
70 {
71 Console.WriteLine("Draw a" + _name.ToString());
72 }
73 }
這種方式屬於安全式的Composite模式,在這種方式下,雖然避免了前面所討論的錯誤,可是它也使得葉子節點和樹枝構件具備不同的接口。這種方式和透明式的Composite各有優劣,具體使用哪個,須要根據問題的實際狀況而定。經過Composite模式,客戶程序在調用Draw()的時候不用再去判斷複雜圖像元素中的子對象究竟是基本圖像元素,仍是複雜圖像元素,看一下簡單的客戶端調用:
1 public class App
2 {
3 public static void Main()
4 {
5 Picture root = new Picture("Root");
6
7 root.Add(new Line("Line"));
8 root.Add(new Circle("Circle"));
9
10 Rectangle r = new Rectangle("Rectangle");
11 root.Add(r);
12
13 root.Draw();
Composite模式實現要點:
1.Composite模式採用樹形結構來實現廣泛存在的對象容器,從而將「一對多」的關係轉化「一對一」的關係,使得客戶代碼能夠一致地處理對象和對象容器,無需關心處理的是單個的對象,仍是組合的對象容器。
2.將「客戶代碼與複雜的對象容器結構」解耦是Composite模式的核心思想,解耦以後,客戶代碼將與純粹的抽象接口——而非對象容器的復內部實現結構——發生依賴關係,從而更能「應對變化」。
3.Composite模式中,是將「Add和Remove等和對象容器相關的方法」定義在「表示抽象對象的Component類」中,仍是將其定義在「表示對象容器的Composite類」中,是一個關乎「透明性」和「安全性」的兩難問題,須要仔細權衡。這裏有可能違背面向對象的「單一職責原則」,可是對於這種特殊結構,這又是必須付出的代價。ASP.NET控件的實如今這方面爲咱們提供了一個很好的示範。
4.Composite模式在具體實現中,可讓父對象中的子對象反向追溯;若是父對象有頻繁的遍歷需求,可以使用緩存技巧來改善效率。
動機(Motivate):
在軟件開發系統中,客戶程序常常會與複雜系統的內部子系統之間產生耦合,而致使客戶程序隨着子系統的變化而變化。那麼如何簡化客戶程序與子系統之間的交互接口?如何將複雜系統的內部子系統與客戶程序之間的依賴解耦?
意圖(Intent):
爲子系統中的一組接口提供一個一致的界面,Facade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用。
--------《設計模式》GOF
結構圖(Struct):
適用性:
1.爲一個複雜子系統提供一個簡單接口。
2.提升子系統的獨立性。
3.在層次化結構中,能夠使用Facade模式定義系統中每一層的入口。
生活中的例子:
代碼實現:
咱們平時的開發中其實已經不知不覺的在用Façade模式,如今來考慮這樣一個抵押系統,當有一個客戶來時,有以下幾件事情須要確認:到銀行子系統查詢他是否有足夠多的存款,到信用子系統查詢他是否有良好的信用,到貸款子系統查詢他有無貸款劣跡。只有這三個子系統都經過時纔可進行抵押。咱們先不考慮Façade模式,那麼客戶程序就要直接訪問這些子系統,分別進行判斷。類結構圖下:
在這個程序中,咱們首先要有一個顧客類,它是一個純數據類,並沒有任何操做,示意代碼:
1 //顧客類
2 public class Customer
3 {
4 private string _name;
5
6 public Customer(string name)
7 {
8 this._name = name;
9 }
10
11 public string Name
12 {
13 get { return _name; }
14 }
15 }
下面這三個類均是子系統類,示意代碼:
1 //銀行子系統
2 public class Bank
3 {
4 public bool HasSufficientSavings(Customer c, int amount)
5 {
6 Console.WriteLine("Check bank for " + c.Name);
7 return true;
8 }
9 }
10
11 //信用子系統
12 public class Credit
13 {
14 public bool HasGoodCredit(Customer c)
15 {
16 Console.WriteLine("Check credit for " + c.Name);
17 return true;
18 }
19 }
20
21 //貸款子系統
22 public class Loan
23 {
24 public bool HasNoBadLoans(Customer c)
25 {
26 Console.WriteLine("Check loans for " + c.Name);
27 return true;
28 }
29 }
看客戶程序的調用:
1 //客戶程序
2 public class MainApp
3 {
4 private const int _amount = 12000;
5
6 public static void Main()
7 {
8 Bank bank = new Bank();
9 Loan loan = new Loan();
10 Credit credit = new Credit();
11
12 Customer customer = new Customer("Ann McKinsey");
13
14 bool eligible = true;
15
16 if (!bank.HasSufficientSavings(customer, _amount))
17 {
18 eligible = false;
19 }
20 else if (!loan.HasNoBadLoans(customer))
21 {
22 eligible = false;
23 }
24 else if (!credit.HasGoodCredit(customer))
25 {
26 eligible = false;
27 }
28
29 Console.WriteLine("\n" + customer.Name + " has been " + (eligible ? "Approved" : "Rejected"));
30 Console.ReadLine();
31 }
32 }
能夠看到,在不用Façade模式的狀況下,客戶程序與三個子系統都發生了耦合,這種耦合使得客戶程序依賴於子系統,當子系統化時,客戶程序也將面臨不少變化的挑戰。一個合情合理的設計就是爲這些子系統建立一個統一的接口,這個接口簡化了客戶程序的判斷操做。看一下引入Façade模式後的類結構圖:
變
外觀類Mortage的實現以下:
1 /外觀類
2 public class Mortgage
3 {
4 private Bank bank = new Bank();
5 private Loan loan = new Loan();
6 private Credit credit = new Credit();
7
8 public bool IsEligible(Customer cust, int amount)
9 {
10 Console.WriteLine("{0} applies for {1:C} loan\n",
11 cust.Name, amount);
12
13 bool eligible = true;
14
15 if (!bank.HasSufficientSavings(cust, amount))
16 {
17 eligible = false;
18 }
19 else if (!loan.HasNoBadLoans(cust))
20 {
21 eligible = false;
22 }
23 else if (!credit.HasGoodCredit(cust))
24 {
25 eligible = false;
26 }
27
28 return eligible;
29 }
30 }
顧客類和子系統類的實現仍然以下:
1 //銀行子系統
2 public class Bank
3 {
4 public bool HasSufficientSavings(Customer c, int amount)
5 {
6 Console.WriteLine("Check bank for " + c.Name);
7 return true;
8 }
9 }
10
11 //信用證子系統
12 public class Credit
13 {
14 public bool HasGoodCredit(Customer c)
15 {
16 Console.WriteLine("Check credit for " + c.Name);
17 return true;
18 }
19 }
20
21 //貸款子系統
22 public class Loan
23 {
24 public bool HasNoBadLoans(Customer c)
25 {
26 Console.WriteLine("Check loans for " + c.Name);
27 return true;
28 }
29 }
30
31 //顧客類
32 public class Customer
33 {
34 private string name;
35
36 public Customer(string name)
37 {
38 this.name = name;
39 }
40
41 public string Name
42 {
43 get { return name; }
44 }
45 }
而此時客戶程序的實現:
1 //客戶程序類
2 public class MainApp
3 {
4 public static void Main()
5 {
6 //外觀
7 Mortgage mortgage = new Mortgage();
8
9 Customer customer = new Customer("Ann McKinsey");
10 bool eligable = mortgage.IsEligible(customer, 125000);
11
12 Console.WriteLine("\n" + customer.Name +
13 " has been " + (eligable ? "Approved" : "Rejected"));
14 Console.ReadLine();
15 }
16 }
能夠看到引入Façade模式後,客戶程序只與Mortgage發生依賴,也就是Mortgage屏蔽了子系統之間的複雜的操做,達到了解耦內部子系統與客戶程序之間的依賴。
.NET架構中的Façade模式
Façade模式在實際開發中最多的運用當屬開發N層架構的應用程序了,一個典型的N層結構以下:
在這個架構中,總共分爲四個邏輯層,分別爲:用戶層UI,業務外觀層Business Façade,業務規則層Business Rule,數據訪問層Data Access。其中Business Façade層的職責以下:
l 從「用戶」層接收用戶輸入
l 若是請求須要對數據進行只讀訪問,則可能使用「數據訪問」層
l 將請求傳遞到「業務規則」層
l 將響應從「業務規則」層返回到「用戶」層
l 在對「業務規則」層的調用之間維護臨時狀態
對這一架構最好的體現就是Duwamish示例了。在該應用程序中,有部分操做只是簡單的從數據庫根據條件提取數據,不須要通過任何處理,而直接將數據顯示到網頁上,好比查詢某類別的圖書列表。而另一些操做,好比計算定單中圖書的總價並根據顧客的級別計算回扣等等,這部分每每有許多不一樣的功能的類,操做起來也比較複雜。若是採用傳統的三層結構,這些商業邏輯通常是會放在中間層,那麼對內部的這些大量種類繁多,使用方法也各異的不一樣的類的調用任務,就徹底落到了表示層。這樣勢必會增長表示層的代碼量,將表示層的任務複雜化,和表示層只負責接受用戶的輸入並返回結果的任務不太相稱,並增長了層與層之間的耦合程度。因而就引入了一個Façade層,讓這個Facade來負責管理系統內部類的調用,併爲表示層提供了一個單一而簡單的接口。看一下Duwamish結構圖:
從圖中能夠看到,UI層將請求發送給業務外觀層,業務外觀層對請求進行初步的處理,判斷是否須要調用業務規則層,仍是直接調用數據訪問層獲取數據。最後由數據訪問層訪問數據庫並按照來時的步驟返回結果到UI層,來看具體的代碼實現。
在獲取商品目錄的時候,Web UI調用業務外觀層:
1 productSystem = new ProductSystem();
2 categorySet = productSystem.GetCategories(categoryID);
業務外觀層直接調用了數據訪問層:
1 public CategoryData GetCategories(int categoryId)
2 {
3 //
4 // Check preconditions
5 //
6 ApplicationAssert.CheckCondition(categoryId >= 0,"Invalid Category Id",ApplicationAssert.LineNumber);
7 //
8 // Retrieve the data
9 //
10 using (Categories accessCategories = new Categories())
11 {
12 return accessCategories.GetCategories(categoryId);
13 }
14
15 }
在添加訂單時,UI調用業務外觀層:
1 public void AddOrder()
2 {
3 ApplicationAssert.CheckCondition(cartOrderData != null, "Order requires data", ApplicationAssert.LineNumber);
4
5 //Write trace log.
6 ApplicationLog.WriteTrace("Duwamish7.Web.Cart.AddOrder:\r\nCustomerId: " +
7 cartOrderData.Tables[OrderData.CUSTOMER_TABLE].Rows[0][OrderData.PKID_FIELD].ToString());
8 cartOrderData = (new OrderSystem()).AddOrder(cartOrderData);
9 }
業務外觀層調用業務規則層:
1 public OrderData AddOrder(OrderData order)
2 {
3 //
4 // Check preconditions
5 //
6 ApplicationAssert.CheckCondition(order != null, "Order is required", ApplicationAssert.LineNumber);
7
8 (new BusinessRules.Order()).InsertOrder(order);
9 return order;
10 }
業務規則層進行復雜的邏輯處理後,再調用數據訪問層:
1 public OrderData AddOrder(OrderData order)
2 {
3 //
4 // Check preconditions
5 //
6 ApplicationAssert.CheckCondition(order != null, "Order is required", ApplicationAssert.LineNumber);
7
8 (new BusinessRules.Order()).InsertOrder(order);
9 return order;
10 }
11
12
13 業務規則層進行復雜的邏輯處理後,再調用數據訪問層:
14 public bool InsertOrder(OrderData order)
15 {
16 //
17 // Assume it's good
18 //
19 bool isValid = true;
20 //
21 // Validate order summary
22 //
23 DataRow summaryRow = order.Tables[OrderData.ORDER_SUMMARY_TABLE].Rows[0];
24
25 summaryRow.ClearErrors();
26
27 if (CalculateShipping(order) != (Decimal)(summaryRow[OrderData.SHIPPING_HANDLING_FIELD]))
28 {
29 summaryRow.SetColumnError(OrderData.SHIPPING_HANDLING_FIELD, OrderData.INVALID_FIELD);
30 isValid = false;
31 }
32
33 if (CalculateTax(order) != (Decimal)(summaryRow[OrderData.TAX_FIELD]))
34 {
35 summaryRow.SetColumnError(OrderData.TAX_FIELD, OrderData.INVALID_FIELD);
36 isValid = false;
37 }
38 //
39 // Validate shipping info
40 //
41 isValid &= IsValidField(order, OrderData.SHIPPING_ADDRESS_TABLE, OrderData.SHIP_TO_NAME_FIELD, 40);
42 //
43 // Validate payment info
44 //
45 DataRow paymentRow = order.Tables[OrderData.PAYMENT_TABLE].Rows[0];
46
47 paymentRow.ClearErrors();
48
49 isValid &= IsValidField(paymentRow, OrderData.CREDIT_CARD_TYPE_FIELD, 40);
50 isValid &= IsValidField(paymentRow, OrderData.CREDIT_CARD_NUMBER_FIELD, 32);
51 isValid &= IsValidField(paymentRow, OrderData.EXPIRATION_DATE_FIELD, 30);
52 isValid &= IsValidField(paymentRow, OrderData.NAME_ON_CARD_FIELD, 40);
53 isValid &= IsValidField(paymentRow, OrderData.BILLING_ADDRESS_FIELD, 255);
54 //
55 // Validate the order items and recalculate the subtotal
56 //
57 DataRowCollection itemRows = order.Tables[OrderData.ORDER_ITEMS_TABLE].Rows;
58
59 Decimal subTotal = 0;
60
61 foreach (DataRow itemRow in itemRows)
62 {
63 itemRow.ClearErrors();
64
65 subTotal += (Decimal)(itemRow[OrderData.EXTENDED_FIELD]);
66
67 if ((Decimal)(itemRow[OrderData.PRICE_FIELD]) <= 0)
68 {
69 itemRow.SetColumnError(OrderData.PRICE_FIELD, OrderData.INVALID_FIELD);
70 isValid = false;
71 }
72
73 if ((short)(itemRow[OrderData.QUANTITY_FIELD]) <= 0)
74 {
75 itemRow.SetColumnError(OrderData.QUANTITY_FIELD, OrderData.INVALID_FIELD);
76 isValid = false;
77 }
78 }
79 //
80 // Verify the subtotal
81 //
82 if (subTotal != (Decimal)(summaryRow[OrderData.SUB_TOTAL_FIELD]))
83 {
84 summaryRow.SetColumnError(OrderData.SUB_TOTAL_FIELD, OrderData.INVALID_FIELD);
85 isValid = false;
86 }
87
88 if ( isValid )
89 {
90 using (DataAccess.Orders ordersDataAccess = new DataAccess.Orders())
91 {
92 return (ordersDataAccess.InsertOrderDetail(order)) > 0;
93 }
94 }
95 else
96 return false;
97 }
Facade模式的個要點:
從客戶程序的角度來看,Facade模式不只簡化了整個組件系統的接口,同時對於組件內部與外部客戶程序來講,從某種程度上也達到了一種「解耦」的效果----內部子系統的任何變化不會影響到Facade接口的變化。
Facade設計模式更注重從架構的層次去看整個系統,而不是單個類的層次。Facdae不少時候更是一種架構
設計模式。
注意區分Facade模式、Adapter模式、Bridge模式與Decorator模式。Facade模式注重簡化接口,Adapter模式注重轉換接口,Bridge模式注重分離接口(抽象)與其實現,Decorator模式注重穩定接口的前提下爲對象擴展功能。
LINQ是什麼?
它是Language Integrated Query。
當咱們要對數據庫表進行查詢的時候,咱們必定會編寫 "select * from sometable where ID = .."的語句。好,那咱們如今根據LINQ的語法,徹底能夠將咱們熟悉的SQL中像"select","from","where"等語句在.NET Framework環境中順利使用而且大大提升開發的效率。
下面我就牛刀小試,作個demo看看。
1. 先下載LinQ框架
如今最新版本是2006年5月發佈"Orcas CTP", 下載地址(這裏 )
2. 下載安裝待完畢。
3. 新建一個"LINQ Console Application"項目。
4. 輸入代碼以下:
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Query;
5 using System.Xml.XLinq;
6 using System.Data.DLinq;
7
8 namespace LINQConsoleApplication1
9 {
10 class Program
11 {
12 static void Main(string[] args)
13 {
14 string[] aBunchOfWords = {"One","Two", "Hello", "World",
15
16 "Four", "Five"};
17 var result =
18 from s in aBunchOfWords // query the string array
19 where s.Length == 5 // for all words with length = 5
20 select s; // and return the string
21 foreach (var s in result) {
22 Console.WriteLine(s); //print
23 }
24 }
25 }
26 }
運行結果以下:
Hello
World
print any key to continue ...
面向對象的代價
面向對象很好地解決了系統抽象性的問題,同時在大多數狀況下,也不會損及系統的性能。可是,在
某些特殊的應用中下,因爲對象的數量太大,採用面向對象會給系統帶來難以承受的內存開銷。好比:
圖形應用中的圖元等對象、字處理應用中的字符對象等。
動機(Motivate):
採用純粹對象方案的問題在於大量細粒度的對象會很快充斥在系統中,從而帶來很高的運行時代價--------主要指內存需求方面的代價。
如何在避免大量細粒度對象問題的同時,讓外部客戶程序仍然可以透明地使用面向對象的方式來進行操做?
意圖(Intent):
運用共享技術有效地支持大量細粒度的對象。 -------《設計模式》GOF
結構(Struct):
適用性:
當如下全部的條件都知足時,能夠考慮使用享元模式:
1、 一個系統有大量的對象。
2、 這些對象耗費大量的內存。
3、 這些對象的狀態中的大部分均可之外部化。
4、 這些對象能夠按照內蘊狀態分紅不少的組,當把外蘊對象從對象中剔除時,每個組均可以僅用一個對象代替。
5、 軟件系統不依賴於這些對象的身份,換言之,這些對象能夠是不可分辨的。
知足以上的這些條件的系統能夠使用享元對象。最後,使用享元模式須要維護一個記錄了系統已有的全部享元的表,而這須要耗費資源。所以,應當在有足夠多的享元實例可供共享時才值得使用享元模式。
生活中的例子:
享元模式使用共享技術有效地支持大量細粒度的對象。公共交換電話網(PSTN)是享元的一個例子。有一些資源例如撥號音發生器、振鈴發生器和撥號接收器是必須由全部用戶共享的。當一個用戶拿起聽筒打電話時,他不須要知道使用了多少資源。對於用戶而言全部的事情就是有撥號音,撥打號碼,撥通電話。
代碼實現:
Flyweight在拳擊比賽中指最輕量級,即「蠅量級」,這裏翻譯爲「享元」,能夠理解爲共享元對象(細粒度對象)的意思。提到Flyweight模式都會通常都會用編輯器例子來講明,這裏也不例外,但我會嘗試着經過重構來看待Flyweight模式。考慮這樣一個字處理軟件,它須要處理的對象可能有單個的字符,由字符組成的段落以及整篇文檔,根據面向對象的設計思想和Composite模式,無論是字符仍是段落,文檔都應該做爲單個的對象去看待,這裏只考慮單個的字符,不考慮段落及文檔等對象,因而能夠很容易的獲得下面的結構圖:
1 // "Charactor"
2 public abstract class Charactor
3 {
4 //Fields
5 protected char _symbol;
6
7 protected int _width;
8
9 protected int _height;
10
11 protected int _ascent;
12
13 protected int _descent;
14
15 protected int _pointSize;
16
17 //Method
18 public abstract void Display();
19 }
20
21 // "CharactorA"
22 public class CharactorA : Charactor
23 {
24 // Constructor
25 public CharactorA()
26 {
27 this._symbol = 'A';
28 this._height = 100;
29 this._width = 120;
30 this._ascent = 70;
31 this._descent = 0;
32 this._pointSize = 12;
33 }
34
35 //Method
36 public override void Display()
37 {
38 Console.WriteLine(this._symbol);
39 }
40 }
41
42 // "CharactorB"
43 public class CharactorB : Charactor
44 {
45 // Constructor
46 public CharactorB()
47 {
48 this._symbol = 'B';
49 this._height = 100;
50 this._width = 140;
51 this._ascent = 72;
52 this._descent = 0;
53 this._pointSize = 10;
54 }
55
56 //Method
57 public override void Display()
58 {
59 Console.WriteLine(this._symbol);
60 }
61 }
62
63 // "CharactorC"
64 public class CharactorC : Charactor
65 {
66 // Constructor
67 public CharactorC()
68 {
69 this._symbol = 'C';
70 this._height = 100;
71 this._width = 160;
72 this._ascent = 74;
73 this._descent = 0;
74 this._pointSize = 14;
75 }
76
77 //Method
78 public override void Display()
79 {
80 Console.WriteLine(this._symbol);
81 }
82 }
好了,如今看到的這段代碼能夠說是很好地符合了面向對象的思想,可是同時咱們也爲此付出了沉重的代價,那就是性能上的開銷,能夠想象,在一篇文檔中,字符的數量遠不止幾百個這麼簡單,可能上千上萬,內存中就同時存在了上千上萬個Charactor對象,這樣的內存開銷是可想而知的。進一步分析能夠發現,雖然咱們須要的Charactor實例很是多,這些實例之間只不過是狀態不一樣而已,也就是說這些實例的狀態數量是不多的。因此咱們並不須要這麼多的獨立的Charactor實例,而只須要爲每一種Charactor狀態建立一個實例,讓整個字符處理軟件共享這些實例就能夠了。看這樣一幅示意圖:
如今咱們看到的A,B,C三個字符是共享的,也就是說若是文檔中任何地方須要這三個字符,只須要使用共享的這三個實例就能夠了。然而咱們發現單純的這樣共享也是有問題的。雖然文檔中的用到了不少的A字符,雖然字符的symbol等是相同的,它能夠共享;可是它們的pointSize倒是不相同的,即字符在文檔中中的大小是不相同的,這個狀態不能夠共享。爲解決這個問題,首先咱們將不可共享的狀態從類裏面剔除出去,即去掉pointSize這個狀態(只是暫時的J),類結構圖以下所示:
1 // "Charactor"
2 public abstract class Charactor
3 {
4 //Fields
5 protected char _symbol;
6
7 protected int _width;
8
9 protected int _height;
10
11 protected int _ascent;
12
13 protected int _descent;
14
15 //Method
16 public abstract void Display();
17 }
18
19 // "CharactorA"
20 public class CharactorA : Charactor
21 {
22 // Constructor
23 public CharactorA()
24 {
25 this._symbol = 'A';
26 this._height = 100;
27 this._width = 120;
28 this._ascent = 70;
29 this._descent = 0;
30 }
31
32 //Method
33 public override void Display()
34 {
35 Console.WriteLine(this._symbol);
36 }
37 }
38
39 // "CharactorB"
40 public class CharactorB : Charactor
41 {
42 // Constructor
43 public CharactorB()
44 {
45 this._symbol = 'B';
46 this._height = 100;
47 this._width = 140;
48 this._ascent = 72;
49 this._descent = 0;
50 }
51
52 //Method
53 public override void Display()
54 {
55 Console.WriteLine(this._symbol);
56 }
57 }
58
59 // "CharactorC"
60 public class CharactorC : Charactor
61 {
62 // Constructor
63 public CharactorC()
64 {
65 this._symbol = 'C';
66 this._height = 100;
67 this._width = 160;
68 this._ascent = 74;
69 this._descent = 0;
70 }
71
72 //Method
73 public override void Display()
74 {
75 Console.WriteLine(this._symbol);
76 }
77 }
好,如今類裏面剩下的狀態均可以共享了,下面咱們要作的工做就是控制Charactor類的建立過程,即若是已經存在了「A」字符這樣的實例,就不須要再建立,直接返回實例;若是沒有,則建立一個新的實例。若是把這項工做交給Charactor類,即Charactor類在負責它自身職責的同時也要負責管理Charactor實例的管理工做,這在必定程度上有可能違背類的單一職責原則,所以,須要一個單獨的類來作這項工做,引入CharactorFactory類,結構圖以下:
1 // "CharactorFactory"
2 public class CharactorFactory
3 {
4 // Fields
5 private Hashtable charactors = new Hashtable();
6
7 // Constructor
8 public CharactorFactory()
9 {
10 charactors.Add("A", new CharactorA());
11 charactors.Add("B", new CharactorB());
12 charactors.Add("C", new CharactorC());
13 }
14
15 // Method
16 public Charactor GetCharactor(string key)
17 {
18 Charactor charactor = charactors[key] as Charactor;
19
20 if (charactor == null)
21 {
22 switch (key)
23 {
24 case "A": charactor = new CharactorA(); break;
25 case "B": charactor = new CharactorB(); break;
26 case "C": charactor = new CharactorC(); break;
27 //
28 }
29 charactors.Add(key, charactor);
30 }
31 return charactor;
32 }
33 }
到這裏已經徹底解決了能夠共享的狀態(這裏很醜陋的一個地方是出現了switch語句,但這能夠經過別的辦法消除,爲了簡單期間咱們先保持這種寫法)。下面的工做就是處理剛纔被咱們剔除出去的那些不可共享的狀態,由於雖然將那些狀態移除了,可是Charactor對象仍然須要這些狀態,被咱們剝離後這些對象根本就沒法工做,因此須要將這些狀態外部化。首先會想到一種比較簡單的解決方案就是對於不能共享的那些狀態,不須要去在Charactor類中設置,而直接在客戶程序代碼中進行設置,類結構圖以下:
1 public class Program
2 {
3 public static void Main()
4 {
5 Charactor ca = new CharactorA();
6 Charactor cb = new CharactorB();
7 Charactor cc = new CharactorC();
8
9 //顯示字符
10
11 //設置字符的大小ChangeSize();
12 }
13
14 public void ChangeSize()
15 {
16 //在這裏設置字符的大小
17 }
18 }
按照這樣的實現思路,能夠發現若是有多個客戶端程序使用的話,會出現大量的重複性的邏輯,用重構的術語來講是出現了代碼的壞味道,不利於代碼的複用和維護;另外把這些狀態和行爲移到客戶程序裏面破壞了封裝性的原則。再次轉變咱們的實現思路,能夠肯定的是這些狀態仍然屬於Charactor對象,因此它仍是應該出如今Charactor類中,對於不一樣的狀態能夠採起在客戶程序中經過參數化的方式傳入。類結構圖以下:
1 // "Charactor"
2 public abstract class Charactor
3 {
4 //Fields
5 protected char _symbol;
6
7 protected int _width;
8
9 protected int _height;
10
11 protected int _ascent;
12
13 protected int _descent;
14
15 protected int _pointSize;
16
17 //Method
18 public abstract void SetPointSize(int size);
19 public abstract void Display();
20 }
21
22 // "CharactorA"
23 public class CharactorA : Charactor
24 {
25 // Constructor
26 public CharactorA()
27 {
28 this._symbol = 'A';
29 this._height = 100;
30 this._width = 120;
31 this._ascent = 70;
32 this._descent = 0;
33 }
34
35 //Method
36 public override void SetPointSize(int size)
37 {
38 this._pointSize = size;
39 }
40
41 public override void Display()
42 {
43 Console.WriteLine(this._symbol +
44 "pointsize:" + this._pointSize);
45 }
46 }
47
48 // "CharactorB"
49 public class CharactorB : Charactor
50 {
51 // Constructor
52 public CharactorB()
53 {
54 this._symbol = 'B';
55 this._height = 100;
56 this._width = 140;
57 this._ascent = 72;
58 this._descent = 0;
59 }
60
61 //Method
62 public override void SetPointSize(int size)
63 {
64 this._pointSize = size;
65 }
66
67 public override void Display()
68 {
69 Console.WriteLine(this._symbol +
70 "pointsize:" + this._pointSize);
71 }
72 }
73
74 // "CharactorC"
75 public class CharactorC : Charactor
76 {
77 // Constructor
78 public CharactorC()
79 {
80 this._symbol = 'C';
81 this._height = 100;
82 this._width = 160;
83 this._ascent = 74;
84 this._descent = 0;
85 }
86
87 //Method
88 public override void SetPointSize(int size)
89 {
90 this._pointSize = size;
91 }
92
93 public override void Display()
94 {
95 Console.WriteLine(this._symbol +
96 "pointsize:" + this._pointSize);
97 }
98 }
99
100 // "CharactorFactory"
101 public class CharactorFactory
102 {
103 // Fields
104 private Hashtable charactors = new Hashtable();
105
106 // Constructor
107 public CharactorFactory()
108 {
109 charactors.Add("A", new CharactorA());
110 charactors.Add("B", new CharactorB());
111 charactors.Add("C", new CharactorC());
112 }
113
114 // Method
115 public Charactor GetCharactor(string key)
116 {
117 Charactor charactor = charactors[key] as Charactor;
118
119 if (charactor == null)
120 {
121 switch (key)
122 {
123 case "A": charactor = new CharactorA(); break;
124 case "B": charactor = new CharactorB(); break;
125 case "C": charactor = new CharactorC(); break;
126 //
127 }
128 charactors.Add(key, charactor);
129 }
130 return charactor;
131 }
132 }
133
134 public class Program
135 {
136 public static void Main()
137 {
138 CharactorFactory factory = new CharactorFactory();
139
140 // Charactor "A"
141 CharactorA ca = (CharactorA)factory.GetCharactor("A");
142 ca.SetPointSize(12);
143 ca.Display();
144
145 // Charactor "B"
146 CharactorB cb = (CharactorB)factory.GetCharactor("B");
147 ca.SetPointSize(10);
148 ca.Display();
149
150 // Charactor "C"
151 CharactorC cc = (CharactorC)factory.GetCharactor("C");
152 ca.SetPointSize(14);
153 ca.Display();
154 }
155 }
能夠看到這樣的實現明顯優於第一種實現思路。好了,到這裏咱們就到到了經過Flyweight模式實現了優化資源的這樣一個目的。在這個過程當中,還有以下幾點須要說明:
1.引入CharactorFactory是個關鍵,在這裏建立對象已經不是new一個Charactor對象那麼簡單,而必須用工廠方法封裝起來。
2.在這個例子中把Charactor對象做爲Flyweight對象是否準確值的考慮,這裏只是爲了說明Flyweight模式,至於在實際應用中,哪些對象須要做爲Flyweight對象是要通過很好的計算得知,而毫不是憑空臆想。
3.區份內外部狀態很重要,這是享元對象能作到享元的關鍵所在。
到這裏,其實咱們的討論尚未結束。有人可能會提出以下問題,享元對象(Charactor)在這個系統中相對於每個內部狀態而言它是惟一的,這跟單件模式有什麼區別呢?這個問題已經很好回答了,那就是單件類是不能直接被實例化的,而享元類是能夠被實例化的。事實上在這裏面真正被設計爲單件的應該是享元工廠(不是享元)類,由於若是建立不少個享元工廠的實例,那咱們所作的一切努力都是白費的,並無減小對象的個數。修改後的類結構圖以下:
1 // "CharactorFactory"
2 public class CharactorFactory
3 {
4 // Fields
5 private Hashtable charactors = new Hashtable();
6
7 private CharactorFactory instance;
8 // Constructor
9 private CharactorFactory()
10 {
11 charactors.Add("A", new CharactorA());
12 charactors.Add("B", new CharactorB());
13 charactors.Add("C", new CharactorC());
14 }
15
16 // Property
17 public CharactorFactory Instance
18 {
19 get
20 {
21 if (instance != null)
22 {
23 instance = new CharactorFactory();
24 }
25 return instance;
26 }
27 }
28
29 // Method
30 public Charactor GetCharactor(string key)
31 {
32 Charactor charactor = charactors[key] as Charactor;
33
34 if (charactor == null)
35 {
36 switch (key)
37 {
38 case "A": charactor = new CharactorA(); break;
39 case "B": charactor = new CharactorB(); break;
40 case "C": charactor = new CharactorC(); break;
41 //
42 }
43 charactors.Add(key, charactor);
44 }
45 return charactor;
46 }
47 }
.NET框架中的應用:
Flyweight更多時候的時候一種底層的設計模式,在咱們的實際應用程序中使用的並非不少。在.NET中的String類型其實就是運用了Flyweight模式。能夠想象,若是每次執行string s1 = 「abcd」操做,都建立一個新的字符串對象的話,內存的開銷會很大。因此.NET中若是第一次建立了這樣的一個字符串對象s1,下次再建立相同的字符串s2時只是把它的引用指向「abcd」,這樣就實現了「abcd」在內存中的共享。能夠經過下面一個簡單的程序來演示s1和s2的引用是否一致:
1 public class Program
2 {
3 public static void Main(string[] args)
4 {
5 string s1 = "abcd";
6 string s2 = "abcd";
7
8 Console.WriteLine(Object.ReferenceEquals(s1,s2));
9
10 Console.ReadLine();
11 }
12 }
Flyweight實現要點:
1.面向對象很好的解決了抽象性的問題,可是做爲一個運行在機器中的程序實體,咱們須要考慮對象的代價問題。Flyweight設計模式主要解決面向對象的代價問題,通常不觸及面向對象的抽象性問題。
2.Flyweight採用對象共享的作法來下降系統中對象的個數,從而下降細粒度對象給系統帶來的內存壓力。在具體實現方面,要注意對象狀態的處理。
3.享元模式的優勢在於它大幅度地下降內存中對象的數量。可是,它作到這一點所付出的代價也是很高的:享元模式使得系統更加複雜。爲了使對象能夠共享,須要將一些狀態外部化,這使得程序的邏輯複雜化。另外它將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長。
直接與間接:
人們對複雜的軟件系統常有一種處理手法,即增長一層間接層,從而對系統得到一種更爲靈活、
知足特定需求的解決方案。
動機(Motivate):
在面向對象系統中,有些對象因爲某種緣由(好比對象建立的開銷很大,或者某些操做須要安全控制,或者須要進程外的訪問等),直接訪問會給使用者、或者系統結構帶來不少麻煩。
如何在不失去透明操做對象的同時來管理/控制這些對象特有的複雜性?增長一層間接層是軟件開發中常見的解決方式。
意圖(Intent):
爲其餘對象提供一種代理以控制對這個對象的訪問。 -------《設計模式》GOF
結構圖(Struct):
生活中的例子:
代理模式提供一箇中介以控制對這個對象的訪問。一張支票或銀行存單是帳戶中資金的代理。支票在市場交易中用來代替現金,並提供對簽發人帳號上資金的控制。
代碼實例:
在軟件系統中,咱們無時不在跨越障礙,當咱們訪問網絡上一臺計算機的資源時,咱們正在跨越網絡障礙,當咱們去訪問服務器上數據庫時,咱們又在跨越數據庫訪問障礙,同時還有網絡障礙。跨越這些障礙有時候是很是複雜的,若是咱們更多的去關注處理這些障礙問題,可能就會忽視了原本應該關注的業務邏輯問題,Proxy模式有助於咱們去解決這些問題。咱們以一個簡單的數學計算程序爲例,這個程序只負責進行簡單的加減乘除運算:
1 public class Math
2 {
3 public double Add(double x,double y)
4 {
5 return x + y;
6 }
7
8 public double Sub(double x,double y)
9 {
10 return x - y;
11 }
12
13 public double Mul(double x,double y)
14 {
15 return x * y;
16 }
17
18 public double Dev(double x,double y)
19 {
20 return x / y;
21 }
22 }
若是說這個計算程序部署在咱們本地計算機上,使用就很是之簡單了,咱們也就不用去考慮Proxy模式了。但如今問題是這個Math類並無部署在咱們本地,而是部署在一臺服務器上,也就是說Math類根本和咱們的客戶程序不在同一個地址空間以內,咱們如今要面對的是跨越Internet這樣一個網絡障礙:
這時候調用Math類的方法就沒有下面那麼簡單了,由於咱們更多的還要去考慮網絡的問題,對接收到的結果解包等一系列操做。
1 public class App
2 {
3 public static void Main()
4 {
5 Math math = new Math();
6
7 // 對接收到的結果數據進行解包
8
9 double addresult = math.Add(2,3);
10
11 double subresult = math.Sub(6,4);
12
13 double mulresult = math.Mul(2,3);
14
15 double devresult = math.Dev(2,3);
16 }
17 }
爲了解決因爲網絡等障礙引發複雜性,就引出了Proxy模式,咱們使用一個本地的代理來替Math類打點一切,即爲咱們的系統引入了一層間接層,示意圖以下:
咱們在MathProxy中對實現Math數據類的訪問,讓MathProxy來代替網絡上的Math類,這樣咱們看到MathProxy就好像是本地Math類,它與客戶程序處在了同一地址空間內:
1 public class MathProxy
2 {
3 private Math math = new Math();
4
5 // 如下的方法中,可能不只僅是簡單的調用Math類的方法
6
7 public double Add(double x,double y)
8 {
9 return math.Add(x,y);
10 }
11
12 public double Sub(double x,double y)
13 {
14 return math.Sub(x,y);
15 }
16
17 public double Mul(double x,double y)
18 {
19 return math.Mul(x,y);
20 }
21
22 public double Dev(double x,double y)
23 {
24 return math.Dev(x,y);
25 }
26 }
如今能夠說咱們已經實現了對Math類的代理,存在的一個問題是咱們在MathProxy類中調用了原實現類Math的方法,可是Math並不必定實現了全部的方法,爲了強迫Math類實現全部的方法,另外一方面,爲了咱們更加透明的去操做對象,咱們在Math類和MathProxy類的基礎上加上一層抽象,即它們都實現與IMath接口,示意圖以下:
1 public interface IMath
2 {
3 double Add(double x,double y);
4
5 double Sub(double x,double y);
6
7 double Mul(double x,double y);
8
9 double Dev(double x,double y);
10 }
11
12 Math類和MathProxy類分別實現IMath接口:
13
14 public class MathProxy : IMath
15 {
16 //
17 }
18
19 public class Math : IMath
20 {
21 //
22 }
此時咱們在客戶程序中就能夠像使用Math類同樣來使用MathProxy類了:
1 public class App
2 {
3 public static void Main()
4 {
5 MathProxy proxy = new MathProxy();
6
7 double addresult = proxy.Add(2,3);
8
9 double subresult = proxy.Sub(6,4);
10
11 double mulresult = proxy.Mul(2,3);
12
13 double devresult = proxy.Dev(2,3);
14 }
15 }
到這兒整個使用Proxy模式的過程就完成了,回顧前面咱們的解決方案,無非是在客戶程序和Math類之間加了一個間接層,這也是咱們比較常見的解決問題的手段之一。另外,對於程序中的接口Imath,並非必須的,大多數狀況下,咱們爲了保持對對象操做的透明性,並強制實現類實現代理類所要調用的全部的方法,咱們會讓它們實現與同一個接口。可是咱們說代理類它其實只是在必定程度上表明瞭原來的實現類,因此它們有時候也能夠不實現於同一個接口。
代理模式實現要點:
1.遠程(Remote)代理:爲一個位於不一樣的 地址空間的對象提供一個局域表明對象。這個不一樣的地址空間能夠是在本機器中,也但是在另外一臺機器中。遠程代理又叫作大使(Ambassador)。好處是系統能夠將網絡的細節隱藏起來,使得客戶端沒必要考慮網絡的存在。客戶徹底能夠認爲被代理的對象是局域的而不是遠程的,而代理對象承擔了大部份的網絡通信工做。因爲客戶可能沒有意識到會啓動一個耗費時間的遠程調用,所以客戶沒有必要的思想準備。
2.虛擬(Virtual)代理:根據須要建立一個資源消耗較大的對象,使得此對象只在須要時纔會被真正建立。使用虛擬代理模式的好處就是代理對象能夠在必要的時候纔將被代理的對象加載;代理能夠對加載的過程加以必要的優化。當一個模塊的加載十分耗費資源的狀況下,虛擬代理的好處就很是明顯。
3.Copy-on-Write代理:虛擬代理的一種。把複製(克隆)拖延到只有在客戶端須要時,才真正採起行動。
4.保護(Protect or Access)代理:控制對一個對象的訪問,若是須要,能夠給不一樣的用戶提供不一樣級別的使用權限。保護代理的好處是它能夠在運行時間對用戶的有關權限進行檢查,而後在覈實後決定將調用傳遞給被代理的對象。
5.Cache代理:爲某一個目標操做的結果提供臨時的存儲空間,以便多個客戶端能夠共享這些結果。
6.防火牆(Firewall)代理:保護目標,不讓惡意用戶接近。
7.同步化(Synchronization)代理:使幾個用戶可以同時使用一個對象而沒有衝突。
8.智能引用(Smart Reference)代理:當一個對象被引用時,提供一些額外的操做,好比將對此對象調用的次數記錄下來等。
虛擬方法,由virtual聲明,它容許在派生類中重寫,也能夠不重寫。若是在派生類中重寫時要聲明override.
1 public class myclass
2 {
3 public virtual int myint()
4 {
5 ///函數體;
6 }
7 }
8 class myclass1:myclass
9 {
10 public override int myint()
11 {
12 // 函數休;
13 }
14 }
抽象方法:要求其類必須是抽象類,抽象類及抽象方法由abstract聲明,抽象方法中沒有函數體,必須在派生類中重寫此方法,重寫時也須聲明override.
1 public abstract class myclass
2 {
3 public abstract int myint();
4 }
5 public class myclass1:myclass
6 {
7 public override int myint()
8 {
9 //函數體;
10 }
11 }
[.NET(C#)]
把attribute翻譯成特性,用來標識類,方法
把property翻譯爲屬性,性質,用於存取類的字段
把markup翻譯成標記,tag仍是翻譯成標籤比較好
[.NET(C#)]
.NET Framework的核心是其運行庫的執行環境。
稱爲公共語言運行庫(CLR)或.NET運行庫.
一般將在CLR的控制下運行的代碼稱爲託管代碼(managed code).
在CLR執行開發的源代碼以前,須要編譯它們爲中間語言(IL),CLR再把IL編譯爲平臺專用的代碼。
程序集(assembly)是包含編譯好的,面向.NET Framework的代碼的邏輯單元.
可執行代碼和庫代碼使用相同的程序集結構.
程序集的一個重要特性是它們包含的元數據描述了對應代碼中定義的類型和方法.
[.NET(C#)]
ASP頁面有時顯示比較慢,由於服務器端代碼是解釋性的不是編譯的.
因爲ASP代碼不是結構化的因此難於維護,加上ASP不支持錯誤處理和語法檢查。
而ASP.NET頁面是結構化的。每一個頁面都是一個繼承了.NET類System.Web.UI.Page的類。
另外ASP.NET的後臺編碼功能容許進一步採用結構化的方式.
頁面請求是和WEB服務器在編譯後高速緩存ASP.NET頁面。
[.NET(C#)]
覆蓋(override)和重載(overload):
覆蓋是指子類從新定義父類的虛函數的作法。
重載,是指容許存在多個同名函數,而這些函數的參數表不一樣(或許參數個數不一樣,或許參數類型不一樣,或許二者都不一樣)。
其實,重載的概念並不屬於「面向對象編程」,
重載的實現是:編譯器根據函數不一樣的參數表,對同名函數的名稱作修飾
而後這些同名函數就成了不一樣的函數(至少對於編譯器來講是這樣的)。
如,有兩個同名函數:function func(p:integer):integer; 和function func(p:string):integer;。
那麼編譯器作過修飾後的函數名稱多是這樣的:int_func、str_func。
對於這兩個函數的調用,在編譯器間就已經肯定了,是靜態的(記住:是靜態)。
也就是說,它們的地址在編譯期就綁定了(早綁定),
所以,重載和多態無關!真正和多態相關的是「覆蓋」。
當子類從新定義了父類的虛函數後,父類指針根據賦給它的不一樣的子類指針,動態(記住:是動態!)的調用屬於子類的該函數,
這樣的函數調用在編譯期間是沒法肯定的(調用的子類的虛函數的地址沒法給出)。
所以,這樣的函數地址是在運行期綁定的(晚邦定)。
結論就是:重載只是一種語言特性,與多態無關,與面向對象也無關!
[.NET(C#)]
C#中ref和out的區別:
方法參數上的 out 方法參數關鍵字使方法引用傳遞到方法的同一個變量。
當控制傳遞迴調用方法時,在方法中對參數所作的任何更改都將反映在該變量中。
當但願方法返回多個值時,聲明 out 方法很是有用。
使用 out 參數的方法仍然能夠返回一個值。一個方法能夠有一個以上的 out 參數。
若要使用 out 參數,必須將參數做爲 out 參數顯式傳遞到方法。out 參數的值不會傳遞到 out 參數。
沒必要初始化做爲 out 參數傳遞的變量。然而,必須在方法返回以前爲 out 參數賦值。
屬性不是變量,不能做爲 out 參數傳遞。
方法參數上的 ref 方法參數關鍵字使方法引用傳遞到方法的同一個變量。
當控制傳遞迴調用方法時,在方法中對參數所作的任何更改都將反映在該變量中。
若要使用 ref 參數,必須將參數做爲 ref 參數顯式傳遞到方法。
ref 參數的值被傳遞到 ref 參數。 傳遞到 ref 參數的參數必須最早初始化。
將此方法與 out 參數相比,後者的參數在傳遞到 out 參數以前沒必要顯式初始化。
屬性不是變量,不能做爲 ref 參數傳遞。
二者都是按地址傳遞的,使用後都將改變原來的數值。
ref能夠把參數的數值傳遞進函數,可是out是要把參數清空
就是說你沒法把一個數值從out傳遞進去的,out進去後,參數的數值爲空,因此你必須初始化一次。
兩個的區別:ref是有進有出,out是隻出不進。
[.NET(C#)]
ADO和ADO.NET的區別:
ADO使用OLE DB接口並基於微軟的COM技術
而ADO.NET擁有本身的ADO.NET接口而且基於微軟的.NET體系架構。
ADO以Recordset存儲,而ADO.NET則以DataSet表示。
Recordset看起來更像單表,若是讓Recordset以多表的方式表示就必須在SQL中進行多表鏈接。
反之,DataSet能夠是多個表的集合。ADO 的運做是一種在線方式,這意味着不管是瀏覽或更新數據都必須是實時的。
ADO.NET則使用離線方式,在訪問數據的時候ADO.NET會利用XML製做數據的一份幅本
ADO.NET的數據庫鏈接也只有在這段時間須要在線。
因爲ADO使用COM技術,這就要求所使用的數據類型必須符合COM規範
而ADO.NET基於XML格式,數據類型更爲豐富而且不須要再作COM編排致使的數據類型轉換,從而提升了總體性能。
ADO.NET爲.NET構架提供了優化的數據訪問模型,和基於COM的ADO是徹底兩樣的數據訪問方式。
ado.net與ado存在着比較大的差別:
1.ado.net遵循更通用的原則,不那麼專門面向數據庫。
ado.net集合了全部容許數據處理的類。這些類表示具備典型數據庫功能(如索引,排序和視圖)的數據容器對象。
儘管ado.net是.net數據庫應用程序的權威解決方案
但從整體設計上看,它不像ado數據模型那樣以數據庫爲中心,這是ado.net的一大特色。
2.目前,ado.net提供了兩種數據庫訪問類庫:一種用於sql server 7.0 或更高版本
另外一種用於其餘全部您可能已經安裝的ole db提供程序。
在這兩種狀況下,您分別使用不一樣的類,但遵循類似的命名規則。
除前綴,名稱都是相同的。前一種狀況前綴爲sql,後一種狀況則是oledb。
同時,.net框架還提供了odbc .net的數據訪問模式。
odbc .net data provider是 .net 框架的加強組件,它能夠訪問原始的 odbc 驅動程序
就像 ole db .net data provider 能夠訪問原始的 ole db providers 同樣。
目前它僅在下列驅動程序中測試過:
microsoft sql odbc driver,microsoft odbc driver for oracle,microsoft jet odbc driver。
3.ado.net提供了兩個隊形來處理從數據源中抽取數據,它們是dataset和datareader對象。
前者是記錄在內存中的緩存,您能夠從任何方向隨意訪問和修改。
後者是高度優化的對象,專爲以僅向前方式滾動只讀記錄而設計。
4.ado.net統一了數據容器類編程接口,不管您打算編寫何種應用程序,windows窗體,web窗體仍是web服務
均可以經過同一組類來處理數據。
無論在後端的數據源數sql server數據庫,ole db,xml文件仍是一個數組
您均可以經過相同的方法和屬性來滾動和處理它們的內容。
5.在ado中,xml只不過是輸入和輸出格式。
然而在ado.net中,xml是一種數據格式,提供了操做,組織,共享和傳遞數據的手段。
ADO。NET相對於ADO等主要有什麼改進?
1:ado.net不依賴於ole db提供程序,而是使用.net託管提供的程序,
2:不使用com
3:不在支持動態遊標和服務器端遊
4:,能夠斷開connection而保留當前數據集可用
5:強類型轉換
6:xml支持
[.NET(C#)]
new 關鍵字用法
(1)new 運算符 用於建立對象和調用構造函數。
(2)new 修飾符 用於向基類成員隱藏繼承成員。
(3)new 約束 用於在泛型聲明中約束可能用做類型參數的參數的類型。
指定泛型類聲明中的任何類型參數都必須有公共的無參數構造函數。
[.NET(C#)]
C#中,string str = null 與 string str ="",說明區別。
string str =""初始化對象分配空間
而string str=null初始化對象
[.NET(C#)]
DataGrid的Datasouse能夠鏈接什麼數據源
DataTable DataView DataSet DataViewManager 任何實現IListSource接口的組件 任何實現IList接口的組件
[.NET(C#)]
概述反射和序列化
反射:公共語言運行庫加載器管理應用程序域。
這種管理包括將每一個程序集加載到相應的應用程序域以及控制每一個程序集中類型層次結構的內存佈局。
程序集包含模塊,而模塊包含類型,類型又包含成員。
反射則提供了封裝程序集、模塊和類型的對象。
您能夠使用反射動態地建立類型的實例,將類型綁定到現有對象,或從現有對象中獲取類型。
而後,能夠調用類型的方法或訪問其字段和屬性。
序列化:序列化是將對象狀態轉換爲可保持或傳輸的格式的過程。
與序列化相對的是反序列化,它將流轉換爲對象。
這兩個過程結合起來,能夠輕鬆地存儲和傳輸數據。
[.NET(C#)]
可訪問性級別有哪幾種
public 訪問不受限制。
protected 訪問僅限於包含類或從包含類派生的類型。
internal 訪問僅限於當前程序集。
protected internal 訪問僅限於從包含類派生的當前程序集或類型。
private 訪問僅限於包含類型。
[.NET(C#)]
O/R Mapping 的原理:利用反射,配置將對象和數據庫表映射。
[.NET(C#)]
sealed 修飾符有什麼特色?
sealed 修飾符表示密封,用於類時,表示該類不能再被繼承
不能和 abstract 同時使用,由於這兩個修飾符在含義上互相排斥
用於方法和屬性時,表示該方法或屬性不能再被繼承,必須和 override 關鍵字一塊兒使用
由於使用 sealed 修飾符的方法或屬性確定是基類中相應的虛成員
一般用於實現第三方類庫時不想被客戶端繼承,或用於沒有必要再繼承的類以防止濫用繼承形成層次結構體系混亂
恰當的利用 sealed 修飾符也能夠提升必定的運行效率,由於不用考慮繼承類會重寫該成員
[.NET(C#)]
詳述.NET裏class和struct的異同
結構與類共享幾乎全部相同的語法,但結構比類受到的限制更多:
儘管結構的靜態字段能夠初始化,結構實例字段聲明仍是不能使用初始值設定項。
結構不能聲明默認構造函數(沒有參數的構造函數)或析構函數。
結構的副本由編譯器自動建立和銷燬,所以不須要使用默認構造函數和析構函數。
實際上,編譯器經過爲全部字段賦予默認值(參見默認值表)來實現默認構造函數。
結構不能從類或其餘結構繼承。
結構是值類型 -- 若是從結構建立一個對象並將該對象賦給某個變量,變量則包含結構的所有值。
複製包含結構的變量時,將複製全部數據,對新副本所作的任何修改都不會改變舊副本的數據。
因爲結構不使用引用,所以結構沒有標識 -- 具備相同數據的兩個值類型實例是沒法區分的。
C# 中的全部值類型本質上都繼承自 ValueType,後者繼承自 Object。
編譯器能夠在一個稱爲裝箱的過程當中將值類型轉換爲引用類型。
結構具備如下特色:
結構是值類型,而類是引用類型。
向方法傳遞結構時,結構是經過傳值方式傳遞的,而不是做爲引用傳遞的。
與類不一樣,結構的實例化能夠不使用 new 運算符。
結構能夠聲明構造函數,但它們必須帶參數。
一個結構不能從另外一個結構或類繼承,並且不能做爲一個類的基。
全部結構都直接繼承自 System.ValueType,後者繼承自 System.Object。
結構能夠實現接口。
在結構中初始化實例字段是錯誤的。
類與結構的差異
1. 值類型與引用類型
結構是值類型:值類型在堆棧上分配地址,全部的基類型都是結構類型
例如:int 對應System.int32 結構,string 對應 system.string 結構 ,經過使用結構能夠建立更多的值類型
類是引用類型:引用類型在堆上分配地址 堆棧的執行效率要比堆的執行效率高
但是堆棧的資源有限,不適合處理大的邏輯複雜的對象。
因此結構處理做爲基類型對待的小對象,而類處理某個商業邏輯
由於結構是值類型因此結構之間的賦值能夠建立新的結構,而類是引用類型,類之間的賦值只是複製引用 注:
1.雖然結構與類的類型不同,但是他們的基類型都是對象(object),c#中全部類型的基類型都是object
2.雖然結構的初始化也使用了New 操做符但是結構對象依然分配在堆棧上而不是堆上
若是不使用「新建」(new),那麼在初始化全部字段以前,字段將保持未賦值狀態,且對象不可用
2.繼承性
結構:不能從另一個結構或者類繼承,自己也不能被繼承
雖然結構沒有明確的用sealed聲明,但是結構是隱式的sealed .
類:徹底可擴展的,除非顯示的聲明sealed 不然類能夠繼承其餘類和接口,自身也能被繼承
注:雖然結構不能被繼承 但是結構可以繼承接口,方法和類繼承接口同樣
例如:結構實現接口
interface IImage
{
void Paint();
}
struct Picture : IImage
{
public void Paint()
{
// painting code goes here
}
private int x, y, z; // other struct members
}
3.內部結構:
結構:
沒有默認的構造函數,可是能夠添加構造函數
沒有析構函數
沒有 abstract 和 sealed(由於不能繼承)
不能有protected 修飾符
能夠不使用new 初始化
在結構中初始化實例字段是錯誤的
類:
有默認的構造函數
有析構函數
能夠使用 abstract 和 sealed
有protected 修飾符
必須使用new 初始化
[.NET(C#)]
如何選擇結構仍是類
1. 堆棧的空間有限,對於大量的邏輯的對象,建立類要比建立結構好一些
2. 結構表示如點、矩形和顏色這樣的輕量對象
例如,若是聲明一個含有 1000 個點對象的數組,則將爲引用每一個對象分配附加的內存。
在此狀況下,結構的成本較低。
3. 在表現抽象和多級別的對象層次時,類是最好的選擇
4. 大多數狀況下該類型只是一些數據時,結構時最佳的選擇
[.NET(C#)]
abstract class和interface有什麼區別?
答:聲明方法的存在而不去實現它的類被叫作抽像類(abstract class)
它用於要建立一個體現某些基本行爲的類,併爲該類聲明方法,但不能在該類中實現該類的狀況。
不能建立abstract 類的實例。
然而能夠建立一個變量,其類型是一個抽像類,並讓它指向具體子類的一個實例。
不能有抽像構造函數或抽像靜態方法。
Abstract 類的子類爲它們父類中的全部抽像方法提供實現,不然它們也是抽像類。
取而代之,在子類中實現該方法。
知道其行爲的其它類能夠在類中實現這些方法。
接口(interface)是抽像類的變體。
在接口中,全部方法都是抽像的。
多繼承性可經過實現這樣的接口而得到。
接口中的全部方法都是抽像的,沒有一個有程序體。
接口只能夠定義static final成員變量。
接口的實現與子類類似,除了該實現類不能從接口定義中繼承行爲。
當類實現特殊接口時,它定義(即將程序體給予)全部這種接口的方法。
而後,它能夠在實現了該接口的類的任何對像上調用接口的方法。
因爲有抽像類,它容許使用接口名做爲引用變量的類型。一般的動態聯編將生效。
引用能夠轉換到接口類型或從接口類型轉換,instanceof 運算符能夠用來決定某對象的類是否實現了接口。
接口能夠繼承接口。
抽像類能夠實現(implements)接口
抽像類是否可繼承實體類(concrete class),但前提是實體類必須有明確的構造函數。
[.NET(C#)]
什麼叫應用程序域?什麼是託管代碼?什麼是強類型系統?
什麼是裝箱和拆箱?什麼是重載?CTS、CLS和CLR分別做何解釋?
應用程序域:
應用程序域爲安全性、可靠性、版本控制以及卸載程序集提供了隔離邊界。
應用程序域一般由運行庫宿主建立,運行庫宿主負責在運行應用程序以前引導公共語言運行庫。
應用程序域提供了一個更安全、用途更廣的處理單元,公共語言運行庫可以使用該單元提供應用程序之間的隔離。
應用程序域能夠理解爲一種輕量級進程。起到安全的做用。佔用資源小。
託管代碼:
使用基於公共語言運行庫的語言編譯器開發的代碼稱爲託管代碼;託管代碼具備許多優勢,
例如:跨語言集成、跨語言異常處理、加強的安全性、版本控制和部署支持、簡化的組件交互模型、調試和分析服務等。
裝箱和拆箱:
從值類型接口轉換到引用類型:裝箱。
從引用類型轉換到值類型:拆箱。
裝箱和拆箱使值類型可以被視爲對象。
對值類型裝箱將把該值類型打包到 Object 引用類型的一個實例中。
這使得值類型能夠存儲於垃圾回收堆中。
拆箱將從對象中提取值類型。
重載:
每一個類型成員都有一個惟一的簽名。
方法簽名由方法名稱和一個參數列表(方法的參數的順序和類型)組成。
只要簽名不一樣,就能夠在一種類型內定義具備相同名稱的多種方法。
當定義兩種或多種具備相同名稱的方法時,就稱做重載。
CTS通用類型系統 (common type system) :
一種肯定公共語言運行庫如何定義、使用和管理類型的規範。
CLR公共語言運行庫:
.NET Framework 提供了一個稱爲公共語言運行庫的運行時環境.
它運行代碼並提供使開發過程更輕鬆的服務。
CLS公共語言規範:
要和其餘對象徹底交互,而無論這些對象是以何種語言實現的.
對象必須只向調用方公開那些它們必須與之互用的全部語言的通用功能。
爲此定義了公共語言規範 (CLS),它是許多應用程序所需的一套基本語言功能。
強類型:
C# 是強類型語言;所以每一個變量和對象都必須具備聲明類型。
[.NET(C#)]
值類型和引用類型的區別?
基於值類型的變量直接包含值。
將一個值類型變量賦給另外一個值類型變量時,將複製包含的值。
這與引用類型變量的賦值不一樣,引用類型變量的賦值只複製對對象的引用,而不復制對象自己。
全部的值類型均隱式派生自 System.ValueType。
與引用類型不一樣,從值類型不可能派生出新的類型。但與引用類型相同的是,結構也能夠實現接口。
與引用類型不一樣,值類型不可能包含 null 值。然而,可空類型功能容許將 null 賦給值類型。
每種值類型均有一個隱式的默認構造函數來初始化該類型的默認值。
值類型主要由兩類組成:結構、枚舉
結構分爲如下幾類:
Numeric(數值)類型、整型、浮點型、decimal、bool、用戶定義的結構。
引用類型的變量又稱爲對象,可存儲對實際數據的引用。
聲明引用類型的關鍵字:class、interface、delegate、內置引用類型: object、string
值類型 引用類型
內存分配地點 分配在棧中 分配在堆中
效率 效率高,不須要地址轉換 效率低,須要進行地址轉換
內存回收 使用完後,當即回收 使用完後,不是當即回收,等待GC回收
賦值操做 進行復制,建立一個同值新對象 只是對原有對象的引用
函數參數與返回值 是對象的複製 是原有對象的引用,並不產生新的對象
類型擴展 不易擴展 容易擴展,方便與類型擴展
[.NET(C#)]
如何理解委託
委託相似於 C++ 函數指針,但它是類型安全的。
委託容許將方法做爲參數進行傳遞。
委託可用於定義回調方法。
委託能夠連接在一塊兒;例如,能夠對一個事件調用多個方法。
方法不須要與委託簽名精確匹配。有關更多信息,請參見協變和逆變。
C# 2.0 版引入了匿名方法的概念,此類方法容許將代碼塊做爲參數傳遞,以代替單獨定義的方法。
[.NET(C#)]
C#中的接口和類有什麼異同。
異:
不能直接實例化接口。
接口不包含方法的實現。
接口、類和結構可從多個接口繼承。
可是C# 只支持單繼承:類只能從一個基類繼承實現。
類定義可在不一樣的源文件之間進行拆分。
同:
接口、類和結構可從多個接口繼承。
接口相似於抽象基類:繼承接口的任何非抽象類型都必須實現接口的全部成員。
接口能夠包含事件、索引器、方法和屬性。
一個類能夠實現多個接口。
[.NET(C#)]
ASP.net的身份驗證方式有哪些
Windows 身份驗證提供程序
提供有關如何將 Windows 身份驗證與 Microsoft Internet 信息服務 (IIS) 身份驗證
結合使用來確保 ASP.NET 應用程序安全的信息。
Forms 身份驗證提供程序
提供有關如何使用您本身的代碼建立應用程序特定的登陸窗體並執行身份驗證的信息。
使用 Forms 身份驗證的一種簡便方法是使用 ASP.NET 成員資格和 ASP.NET 登陸控件
它們一塊兒提供了一種只需少許或無需代碼就能夠收集、驗證和管理用戶憑據的方法。
Passport 身份驗證提供程序
提供有關由 Microsoft 提供的集中身份驗證服務的信息,該服務爲成員站點提供單一登陸和核心配置
[.NET(C#)]
活動目錄的做用
Active Directory存儲了有關網絡對象的信息,而且讓管理員和用戶可以輕鬆地查找和使用這些信息。
Active Directory使用了一種結構化的數據存儲方式,並以此做爲基礎對目錄信息進行合乎邏輯的分層組織。
[.NET(C#)]
解釋一下UDDI、WSDL的意義及其做用
UDDI:統一描述、發現和集成協議(UDDI, Universal Description, Discovery and Integration)
是一套基於Web的、分佈式的、爲Web服務提供的信息註冊中心的實現標準規範,
同時也包含一組使企業能將自身提供的Web服務註冊以使得別的企業可以發現的訪問協議的實現標準。
UDDI 提供了一組基於標準的規範用於描述和發現服務,還提供了一組基於因特網的實現。
WSDL:WSDL描述Web服務的公共接口。
這是一個基於XML的關於如何與Web服務通信和使用的服務描述;
服務 URL 和命名空間
網絡服務的類型
(可能還包括 SOAP 的函數調用,正像我所說過的,WSDL 足夠自如地去描述網絡服務的普遍內容)
有效函數列表
每一個函數的參數
每一個參數的類型
每一個函數的返回值及其數據類型
[.NET(C#)]
什麼是SOAP,有哪些應用。
答:SOAP(Simple Object Access Protocol )簡單對象訪問協議
是在分散或分佈式的環境中交換信息並執行遠程過程調用的協議,是一個基於XML的協議。
使用SOAP,不用考慮任何特定的傳輸協議(最經常使用的仍是HTTP協議)
能夠容許任何類型的對象或代碼,在任何平臺上,以任何一直語言相互通訊。
這種相互通訊採用的是XML格式的消息。
SOAP也被稱做XMLP,爲兩個程序交換信息提供了一種標準的工做機制。
在各種機構之間經過電子方式相互協做的狀況下徹底有必要爲此制定相應的標準。
SOAP描述了把消息捆綁爲XML的工做方式。
它還說明了發送消息的發送方、消息的內容和地址以及發送消息的時間。
SOAP是Web Service的基本通訊協議。
SOAP規範還定義了怎樣用XML來描述程序數據(Program Data),怎樣執行RPC(Remote Procedure Call)。
大多數SOAP解決方案都支持RPC-style應用程序。
SOAP還支持 Document-style應用程序(SOAP消息只包含XML文本信息)。
最後SOAP規範還定義了HTTP消息是怎樣傳輸SOAP消息的。
MSMQ、SMTP、TCP/IP均可以作SOAP的傳輸協議。
SOAP 是一種輕量級協議,用於在分散型、分佈式環境中交換結構化信息。
SOAP 利用 XML 技術定義一種可擴展的消息處理框架,它提供了一種可經過多種底層協議進行交換的消息結構。
這種框架的設計思想是要獨立於任何一種特定的編程模型和其餘特定實現的語義。
SOAP 定義了一種方法以便將 XML 消息從 A 點傳送到 B 點。
爲此,它提供了一種基於 XML 且具備如下特性的消息處理框架:
1) 可擴展
2) 可經過多種底層網絡協議使用
3) 獨立於編程模型。
[.NET(C#)]
如何部署一個ASP.net頁面
VS 2005和VS 2003都有發佈機制。
2003能夠發佈而後再複製部署。
VS2005基本上能夠直接部署到對應位置。
[.NET(C#)]
GC是什麼? 爲何要有GC?
答:GC是垃圾收集器。
程序員不用擔憂內存管理,由於垃圾收集器會自動進行管理。
要請求垃圾收集,能夠調用下面的方法之一:
System.gc()
Runtime.getRuntime().gc()
不過在C#中不能直接實現Finalize方法,而是在析構函數中調用基類的Finalize()方法
[.NET(C#)]
如何理解.net中的垃圾回收機制
.NET Framework 的垃圾回收器管理應用程序的內存分配和釋放。
每次您使用 new 運算符建立對象時,運行庫都從託管堆爲該對象分配內存。
只要託管堆中有地址空間可用,運行庫就會繼續爲新對象分配空間。
可是,內存不是無限大的。最終,垃圾回收器必須執行回收以釋放一些內存。
垃圾回收器優化引擎根據正在進行的分配狀況肯定執行回收的最佳時間。
當垃圾回收器執行回收時,它檢查託管堆中再也不被應用程序使用的對象並執行必要的操做來回收它們佔用的內存。
[.NET(C#)]
列舉ASP.NET 頁面之間傳遞值的幾種方式。 並說出他們的優缺點。
答. 1).使用QueryString, 如....?id=1; response. Redirect()....
2).使用Session變量
3).使用Server.Transfer
session(viewstate) 簡單,但易丟失
application 全局
cookie 簡單,但可能不支持,可能被僞造
input ttype="hidden" 簡單,可能被僞造
url 參數 簡單,顯示於地址欄,長度有限數據庫 穩定,安全,但性能相對弱
[.NET(C#)]
C#中索引器的實現過程,能夠用任何類型進行索引?(好比數字)
[.NET(C#)]
CTS、CLS、CLR分別做何解釋?
CTS:通用語言系統。
CLS:通用語言規範。
CLR:公共語言運行庫。
[.NET(C#)]
.net中讀寫數據庫須要用到那些類?他們的做用?
DataSet: 數據存儲器。
DataCommand: 執行語句命令。
DataAdapter: 數據的集合,用語填充。
[.NET(C#)]
在.net中,配件的意思是:程序集。(中間語言,源數據,資源,裝配清單)
[.NET(C#)]
經常使用的調用WebService的方法有哪些?
答:1.使用WSDL.exe命令行工具。
2.使用VS.NET中的Add Web Reference菜單選項
[.NET(C#)]
微軟.NET 構架下remoting和webservice兩項技術的理解以及實際中的應用。
.net Remoting 的工做原理是:服務器端向客戶端發送一個進程編號,一個程序域編號,以肯定對象的位置。
WS主要是可利用HTTP,穿透防火牆。而Remoting能夠利用TCP/IP,二進制傳送提升效率。
remoting是.net中用來跨越machine,process,appdomain進行方法調用的技術
對於三成結構的程序,就能夠使用remoting技術來構建.
它是分佈應用的基礎技術.至關於之前的DCOM
Web Service是一種構建應用程序的普通模型
並能在全部支持internet網通信的操做系統上實施。
Web Service令基於組件的開發和web的結合達到最佳
[.NET(C#)]
啓動一個線程是用run()仍是start()?
答:啓動一個線程是調用start()方法,使線程所表明的虛擬處理機處於可運行狀態。
這意味着它能夠由JVM調度並執行。
這並不意味着線程就會當即運行。
run()方法能夠產生必須退出的標誌來中止一個線程。
[.NET(C#)]
構造器Constructor是否可被override?
構造器Constructor不能被繼承,所以不能重寫Overriding,但能夠被重載Overloading。
[.NET(C#)]
abstract的method不可同時是static,不可同時是native,不可同時是synchronized
[.NET(C#)]
final, finally, finalize的區別。
final: 修飾符(關鍵字)若是一個類被聲明爲final,意味着它不能再派生出新的子類,不能做爲父類被繼承。
所以 一個類不能既被聲明爲 abstract的,又被聲明爲final的。
將變量或方法聲明爲final,能夠保證它們在使用中不被改變。
被聲明爲final的變量必須在聲明時給定初值,而在之後的引用中只能讀取,不可修改。
被聲明爲 final的方法也一樣只能使用,不能重載
finally: 在異常處理時提供 finally 塊來執行任何清除操做。
若是拋出一個異常,那麼相匹配的 catch 子句就會執行.
而後控制就會進入 finally 塊(若是有的話)。
finalize: 方法名。
Java 技術容許使用 finalize() 方法在垃圾收集器將對像從內存中清除出去以前作必要的清理工做。
這個方法是由垃圾收集器在肯定這個對象沒有被引用時對這個對象調用的。
它是在 Object 類中定義的 ,所以全部的類都繼承了它。
子類覆蓋 finalize() 方法以整理系統資源或者執行其餘清理工做。
finalize() 方法是在垃圾收集器刪除對像以前對這個對象調用的。
[.NET(C#)]
進程和線程的區別:
進程是系統進行資源分配和調度的單位;
線程是CPU調度和分派的單位.
一個進程能夠有多個線程,這些線程共享這個進程的資源。
[.NET(C#)]
堆和棧的區別:
棧:由編譯器自動分配、釋放。在函數體中定義的變量一般在棧上。
堆:通常由程序員分配釋放。用new、malloc等分配內存函數分配獲得的就是在堆上。
[.NET(C#)]
成員變量和成員函數前加static的做用:
它們被稱爲常成員變量和常成員函數,又稱爲類成員變量和類成員函數。
分別用來反映類的狀態。
好比類成員變量能夠用來統計類實例的數量,類成員函數負責這種統計的動做。
[.NET(C#)]
在c#中using和new這兩個關鍵字有什麼意義:
using 引入名稱空間或者使用非託管資源
new 新建實例或者隱藏父類方法
[.NET(C#)]
XML便可擴展標記語言。
eXtensible Markup Language.標記是指計算機所能理解的信息符號
經過此種標記,計算機之間能夠處理包含各類信息的文章等。
如何定義這些標記,便可以選擇國際通用的標記語言
好比HTML,也能夠使用象XML這樣由相關人士自由決定的標記語言,這就是語言的可擴展性。
XML是從SGML中簡化修改出來的。它主要用到的有XML、XSL和XPath等。
[.NET(C#)]
什麼是code-Behind技術。
答:ASPX,RESX和CS三個後綴的文件,這個就是代碼分離.
實現了HTML代碼和服務器代碼分離.方便代碼編寫和整理.
[.NET(C#)]
XML 與 HTML 的主要區別
1. XML是區分大小寫字母的,HTML不區分。
2. 在HTML中,若是上下文清楚地顯示出段落或者列表鍵在何處結尾,
那麼你能夠省略</p>或者</li>之類的結束 標記。
在XML中,絕對不能省略掉結束標記。
3. 在XML中,擁有單個標記而沒有匹配的結束標記的元素必須用一個 / 字符做爲結尾。
這樣分析器就知道不用 查找結束標記了。
4. 在XML中,屬性值必須分裝在引號中。在HTML中,引號是可用可不用的。
5. 在HTML中,能夠擁有不帶值的屬性名。在XML中,全部的屬性都必須帶有相應的值。
[.NET(C#)]
.net的錯誤處理機制是什麼?
答:.net錯誤處理機制採用try->catch->finally結構.
發生錯誤時,層層上拋,直到找到匹配的Catch爲止。
[.NET(C#)]
Static Nested Class 和 Inner Class的不一樣:
Static Nested Class是被聲明爲靜態(static)的內部類,它能夠不依賴於外部類實例被實例化。
而一般的內部類須要在外部類實例化後才能實例化。
[.NET(C#)]
error和exception有什麼區別:
error 表示恢復不是不可能但很困難的狀況下的一種嚴重問題。好比說內存溢出。
不可能期望程序能處理這樣的狀況。
exception 表示一種設計或實現問題。
也就是說,它表示若是程序運行正常,從不會發生的狀況。
[.NET(C#)]
UDP鏈接和TCP鏈接的異同:
前者只管傳,無論數據到不到,無須創建鏈接.後者保證傳輸的數據準確,需要連結.
[.NET(C#)]
C#中全部對象共同的基類是:System.Object.
[.NET(C#)]
System.String 和System.StringBuilder有什麼區別?
System.String是不可變的字符串。String類是final類故不能夠繼承。
System.StringBuilder存放了一個可變的字符串,並提供一些對這個字符串修改的方法。
[.NET(C#)]
const和readonly有什麼區別?
const 能夠用於局部常量
readonly 實際是類的initonly字段,顯然不能是局部的。
無處不在的Template Method
若是你只想掌握一種設計模式,那麼它就是Template Method!
動機(Motivate):
變化 -----是軟件設計的永恆主題,如何管理變化帶來的複雜性?設計模式的藝術性和複雜度就在於如何
分析,並發現系統中的變化和穩定點,並使用特定的設計方法來應對這種變化。
意圖(Intent):
定義一個操做中的算法的骨架,而將一些步驟延遲到子類中。Template Method使得子類能夠不改變一個算法的結構便可重定義該算法的某些特定步驟。 -------《設計模式》GOF
結構圖(Struct):
適用性:
1.一次性實現一個算法的不變的部分,並將可變的行爲留給子類來實現。
2.各子類中公共的行爲應被提取出來並集中到一個公共父類中以免代碼重複。這是Opdyke和Johnson所描述過的「重分解以通常化」的一個很好的例子。首先識別現有代碼中的不一樣之處,而且將不一樣之處分離爲新的操做。最後,用一個調用這些新的操做的模板方法來替換這些不一樣的代碼。
3.控制子類擴展。模板方法只在特定點調用「Hook」操做,這樣就只容許在這些點進行擴展。
生活中的例子:
代碼實現:
假如咱們須要簡單的讀取Northwind數據庫中的表的記錄並顯示出來。對於數據庫操做,咱們知道無論讀取的是哪張表,它通常都應該通過以下這樣的幾步:
1.鏈接數據庫(Connect)
2.執行查詢命令(Select)
3.顯示數據(Display)
4.斷開數據庫鏈接(Disconnect)
這些步驟是固定的,可是對於每一張具體的數據表所執行的查詢倒是不同的。顯然這須要一個抽象角色,給出頂級行爲的實現。以下圖:
Template Method模式的實現方法是從上到下,咱們首先給出頂級框架DataAccessObject的實現邏輯:
1 public abstract class DataAccessObject
2
3 {
4 protected string connectionString;
5
6 protected DataSet dataSet;
7
8 protected virtual void Connect()
9
10 {
11 connectionString =
12
13 "Server=.;User Id=sa;Password=;Database=Northwind";
14
15 }
16
17 protected abstract void Select();
18
19 protected abstract void Display();
20
21
22 protected virtual void Disconnect()
23
24 {
25 connectionString = "";
26 }
27
28 // The "Template Method"
29
30 public void Run()
31
32 {
33 Connect();
34
35 Select();
36
37 Display();
38
39 Disconnect();
40 }
41 }
顯然在這個頂級的框架DataAccessObject中給出了固定的輪廓,方法Run()即是模版方法,Template Method模式也由此而得名。而對於Select()和Display()這兩個抽象方法則留給具體的子類去實現,以下圖:
1 class Categories : DataAccessObject
2
3 {
4 protected override void Select()
5 {
6 string sql = "select CategoryName from Categories";
7
8 SqlDataAdapter dataAdapter = new SqlDataAdapter(
9
10 sql, connectionString);
11
12 dataSet = new DataSet();
13
14 dataAdapter.Fill(dataSet, "Categories");
15
16 }
17
18 protected override void Display()
19
20 {
21
22 Console.WriteLine("Categories ---- ");
23
24 DataTable dataTable = dataSet.Tables["Categories"];
25
26 foreach (DataRow row in dataTable.Rows)
27
28 {
29
30 Console.WriteLine(row["CategoryName"].ToString());
31
32 }
33
34 Console.WriteLine();
35
36 }
37 }
1 class Products : DataAccessObject
2
3 {
4 protected override void Select()
5
6 {
7 string sql = "select top 10 ProductName from Products";
8
9 SqlDataAdapter dataAdapter = new SqlDataAdapter(
10
11 sql, connectionString);
12
13 dataSet = new DataSet();
14
15 dataAdapter.Fill(dataSet, "Products");
16
17 }
18
19 protected override void Display()
20
21 {
22
23 Console.WriteLine("Products ---- ");
24
25 DataTable dataTable = dataSet.Tables["Products"];
26
27 foreach (DataRow row in dataTable.Rows)
28
29 {
30 Console.WriteLine(row["ProductName"].ToString());
31
32 }
33
34 Console.WriteLine();
35
36 }
37
38 }
再來看看客戶端程序的調用,不須要再去調用每個步驟的方法:
1 public class App
2
3 {
4 static void Main()
5 {
6
7 DataAccessObject dao;
8
9
10 dao = new Categories();
11
12 dao.Run();
13
14
15 dao = new Products();
16
17 dao.Run();
18
19 // Wait for user
20
21 Console.Read();
22
23 }
24
25 }
在上面的例子中,須要注意的是:
1.對於Connect()和Disconnect()方法實現爲了virtual,而Select()和Display()方法則爲abstract,這是由於若是這個方法有默認的實現,則實現爲virtual,不然爲abstract。
2.Run()方法做爲一個模版方法,它的一個重要特徵是:在基類裏定義,並且不可以被派生類更改。有時候它是私有方法(private method),但實際上它常常被聲明爲protected。它經過調用其它的基類方法(覆寫過的)來工做,但它常常是做爲初始化過程的一部分被調用的,這樣就不必讓客戶端程序員可以直接調用它了。
3.在一開始咱們提到了無論讀的是哪張數據表,它們都有共同的操做步驟,即共同點。所以能夠說Template Method模式的一個特徵就是剝離共同點。
Template Mehtod實現要點:
1.Template Method模式是一種很是基礎性的設計模式,在面向對象系統中有着大量的應用。它用最簡潔的機制(虛函數的多態性)爲不少應用程序框架提供了靈活的擴展點,是代碼複用方面的基本實現結構。
2.除了能夠靈活應對子步驟的變化外,「不用調用我,讓我來調用你(Don't call me ,let me call you)」的反向控制結構是Template Method的典型應用。「Don’t call me.Let me call you」是指一個父類調用一個子類的操做,而不是相反。
3.在具體實現方面,被Template Method調用的虛方法能夠具備實現,也能夠沒有任何實現(抽象方法,純虛方法),但通常推薦將它們設置爲protected方法。
方法參數上的 out 方法參數關鍵字使方法引用傳遞到方法的同一個變量。
當控制傳遞迴調用方法時,在方法中對參數所作的任何更改都將反映在該變量中。
當但願方法返回多個值時,聲明 out 方法很是有用。
使用 out 參數的方法仍然能夠返回一個值。一個方法能夠有一個以上的 out 參數。
若要使用 out 參數,必須將參數做爲 out 參數顯式傳遞到方法。out 參數的值不會傳遞到 out 參數。
沒必要初始化做爲 out 參數傳遞的變量。然而,必須在方法返回以前爲 out 參數賦值。
屬性不是變量,不能做爲 out 參數傳遞。
方法參數上的 ref 方法參數關鍵字使方法引用傳遞到方法的同一個變量。
當控制傳遞迴調用方法時,在方法中對參數所作的任何更改都將反映在該變量中。
若要使用 ref 參數,必須將參數做爲 ref 參數顯式傳遞到方法。
ref 參數的值被傳遞到 ref 參數。 傳遞到 ref 參數的參數必須最早初始化。
將此方法與 out 參數相比,後者的參數在傳遞到 out 參數以前沒必要顯式初始化。
屬性不是變量,不能做爲 ref 參數傳遞。
二者都是按地址傳遞的,使用後都將改變原來的數值。
ref能夠把參數的數值傳遞進函數,可是out是要把參數清空
就是說你沒法把一個數值從out傳遞進去的,out進去後,參數的數值爲空,因此你必須初始化一次。
兩個的區別:ref是有進有出,out是隻出不進。
代碼實例以下:
1 namespace TestOutAndRef
2 {
3 class TestApp
4 {
5
6 static void outTest(out int x, out int y)
7 {//離開這個函數前,必須對x和y賦值,不然會報錯。
8 //y = x;
9 //上面這行會報錯,由於使用了out後,x和y都清空了,須要從新賦值,即便調用函數前賦過值也不行
10 x = 1;
11 y = 2;
12 }
13 static void refTest(ref int x, ref int y)
14 {
15 x = 1;
16 y = x;
17 }
18
19
20 static public void OutArray(out int[] myArray)
21 {
22 // Initialize the array:
23 myArray = new int[5] { 1, 2, 3, 4, 5 };
24 }
25 public static void FillArray(ref int[] arr)
26 {
27 // Create the array on demand:
28 if (arr == null)
29 arr = new int[10];
30 // Otherwise fill the array:
31 arr[0] = 123;
32 arr[4] = 1024;
33 }
34
35
36 public static void Main()
37 {
38 //out test
39 int a,b;
40 //out使用前,變量能夠不賦值
41 outTest(out a, out b);
42 Console.WriteLine("a={0};b={1}",a,b);
43 int c=11,d=22;
44 outTest(out c, out d);
45 Console.WriteLine("c={0};d={1}",c,d);
46
47 //ref test
48 int m,n;
49 //refTest(ref m, ref n);
50 //上面這行會出錯,ref使用前,變量必須賦值
51
52 int o=11,p=22;
53 refTest(ref o, ref p);
54 Console.WriteLine("o={0};p={1}",o,p);
55
56
57
58 int[] myArray1; // Initialization is not required
59
60 // Pass the array to the callee using out:
61 OutArray(out myArray1);
62
63 // Display the array elements:
64 Console.WriteLine("Array1 elements are:");
65 for (int i = 0; i < myArray1.Length; i++)
66 Console.WriteLine(myArray1[i]);
67
68 // Initialize the array:
69 int[] myArray = { 1, 2, 3, 4, 5 };
70
71 // Pass the array using ref:
72 FillArray(ref myArray);
73
74 // Display the updated array:
75 Console.WriteLine("Array elements are:");
76 for (int i = 0; i < myArray.Length; i++)
77 Console.WriteLine(myArray[i]);
78 }
79 }
80
81 }
運行結果 以下:
New 關鍵詞的三種用法 C#
(1)new 運算符 用於建立對象和調用構造函數。
(2)new 修飾符 用於隱藏基類成員的繼承成員。
(3)new 約束 用於在泛型聲明中約束可能用做類型參數的參數的類型。
new 運算符
1.用於建立對象和調用構造函數
例:Class_Test MyClass = new Class_Test();
2.也用於爲值類型調用默認的構造函數
例:int myInt = new int();
myInt 初始化爲 0,它是 int 類型的默認值。該語句的效果等同於:int myInt = 0;
3.不能重載 new 運算符。
4.若是 new 運算符分配內存失敗,則它將引起 OutOfMemoryException 異常。
new 修飾符
使用 new 修飾符顯式隱藏從基類繼承的成員。若要隱藏繼承的成員,請使用相同名稱在派生類中聲明該成員,並用 new 修飾符修飾它。
請看下面的類:
1 public class MyClass
2
3 {
4
5 public int x;
6
7 public void Invoke() {}
8
9 }
10
在派生類中用 Invoke 名稱聲明成員會隱藏基類中的 Invoke 方法,即:
1 public class MyDerivedC : MyClass
2
3 {
4
5 new public void Invoke() {}
6
7 }
8
可是,由於字段 x 不是經過相似名隱藏的,因此不會影響該字段。
經過繼承隱藏名稱採用下列形式之一:
1.引入類或結構中的常數、指定、屬性或類型隱藏具備相同名稱的全部基類成員。
2.引入類或結構中的方法隱藏基類中具備相同名稱的屬性、字段和類型。同時也隱藏具備相同簽名的全部基類方法。
3.引入類或結構中的索引器將隱藏具備相同名稱的全部基類索引器。
4.在同一成員上同時使用 new 和 override 是錯誤的。
注意:在不隱藏繼承成員的聲明中使用 new 修飾符將生成警告。
示例
在該例中,基類 MyBaseC 和派生類 MyDerivedC 使用相同的字段名 x,從而隱藏了繼承字段的值。該例說明了 new 修飾符的使用。同時也說明了如何使用徹底限定名訪問基類的隱藏成員。
1 using System;
2
3 public class MyBaseC
4
5 {
6
7 public static int x = 55;
8
9 public static int y = 22;
10
11 }
12
13
14
15 public class MyDerivedC : MyBaseC
16
17 {
18
19 new public static int x = 100; // Name hiding
20
21 public static void Main()
22
23 {
24
25 // Display the overlapping value of x:
26
27 Console.WriteLine(x);
28
29
30
31 // Access the hidden value of x:
32
33 Console.WriteLine(MyBaseC.x);
34
35
36
37 // Display the unhidden member y:
38
39 Console.WriteLine(y);
40
41 }
42
43 }
44
輸出
100
55
22
若是移除 new 修飾符,程序將繼續編譯和運行,但您會收到如下警告:
The keyword new is required on 'MyDerivedC.x' because it hides inherited member 'MyBaseC.x'.
若是嵌套類型正在隱藏另外一種類型,以下例所示,也能夠使用 new 修飾符修改此嵌套類型。
示例
在該例中,嵌套類 MyClass 隱藏了基類中具備相同名稱的類。該例不只說明了如何使用徹底限定名訪問隱藏類成員,同時也說明了如何使用 new 修飾符消除警告消息。
1 using System;
2
3 public class MyBaseC
4
5 {
6
7 public class MyClass
8
9 {
10
11 public int x = 200;
12
13 public int y;
14
15 }
16
17 }
18
19
20
21 public class MyDerivedC : MyBaseC
22
23 {
24
25 new public class MyClass // nested type hiding the base type members
26
27 {
28
29 public int x = 100;
30
31 public int y;
32
33 public int z;
34
35 }
36
37
38
39 public static void Main()
40
41 {
42
43 // Creating object from the overlapping class:
44
45 MyClass S1 = new MyClass();
46
47
48
49 // Creating object from the hidden class:
50
51 MyBaseC.MyClass S2 = new MyBaseC.MyClass();
52
53
54
55 Console.WriteLine(S1.x);
56
57 Console.WriteLine(S2.x);
58
59 }
60
61 }
62
輸出
100
200
C#中,string str = null 與 string str =""的區別。
string str =""初始化對象分配空間
而string str=null初始化對象
更詳細的解釋
這樣定義後,str1是一個空字符串,空字符串是一個特殊的字符串,只不過這個字符串的值爲空,在內存中是有準確的指向的。string str2=null,這樣定義後,只是定義了一個string 類的引用,str2並無指向任何地方,在使用前若是不實例化的話,將報錯。
面向對象(Object Oriented,OO)是當前計算機界關心的重點,它是90年代軟件開發方法的主流。面向對象的概念和應用已超越了程序設計和軟件開發,擴展到很寬的範圍。如數據庫系統、交互式界面、應用結構、應用平臺、分佈式系統、網絡管理結構、CAD技術、人工智能等領域。 談到面向對象,這方面的文章很是多。可是,明確地給出對象的定義或說明對象的定義的很是少——至少我如今尚未發現。其初,「面向對象」是專指在程序設計中採用封裝、繼承、抽象等設計方法。但是,這個定義顯然不能再適合如今狀況。面向對象的思想已經涉及到軟件開發的各個方面。如,面向對象的分析(OOA,Object Oriented Analysis),面向對象的設計(OOD,Object Oriented Design)、以及咱們常常說的面向對象的編程實現(OOP,Object Oriented Programming)。許多有關面向對象的文章都只是講述在面向對象的開發中所須要注意的問題或所採用的比較好的設計方法。看這些文章只有真正懂得什麼是對象,什麼是面向對象,才能最大程度地對本身有所裨益。這一點,恐怕對初學者甚至是從事相關工做多年的人員也會對它們的概念模糊不清。
面向對象是當前計算機界關心的重點,它是90年代軟件開發方法的主流。面向對象的概念和應用已超越了程序設計和軟件開發,擴展到很寬的範圍。如數據庫系統、交互式界面、應用結構、應用平臺、分佈式系統、網絡管理結構、CAD技術、人工智能等領域。
1、傳統開發方法存在問題
1.軟件重用性差
重用性是指同一事物不經修改或稍加修改就可屢次重複使用的性質。軟件重用性是軟件工程追求的目標之一。
2.軟件可維護性差
軟件工程強調軟件的可維護性,強調文檔資料的重要性,規定最終的軟件產品應該由完整、一致的配置成分組成。在軟件開發過程當中,始終強調軟件的可讀性、可修改性和可測試性是軟件的重要的質量指標。實踐證實,用傳統方法開發出來的軟件,維護時其費用和成本仍然很高,其緣由是可修改性差,維護困難,致使可維護性差。
3.開發出的軟件不能知足用戶須要
用傳統的結構化開發大型軟件系統涉及各類不一樣領域的知識,在開發需求模糊或需求動態變化的系統時,所開發出的軟件系統每每不能真正知足用戶的須要。
用結構化方法開發的軟件,其穩定性、可修改性和可重用性都比較差,這是由於結構化方法的本質是功能分解,從表明目標系統總體功能的單個處理着手,自頂向下不斷把複雜的處理分解爲子處理,這樣一層一層的分解下去,直到僅剩下若干個容易實現的子處理功能爲止,而後用相應的工具來描述各個最低層的處理。所以,結構化方法是圍繞實現處理功能的「過程」來構造系統的。然而,用戶需求的變化大部分是針對功能的,所以,這種變化對於基於過程的設計來講是災難性的。用這種方法設計出來的系統結構經常是不穩定的 ,用戶需求的變化每每形成系統結構的較大變化,從而須要花費很大代價才能實現這種變化。
2、面向對象的基本概念
(1)對象。
對象是人們要進行研究的任何事物,從最簡單的整數到複雜的飛機等都可看做對象,它不只能表示具體的事物,還能表示抽象的規則、計劃或事件。
(2)對象的狀態和行爲。
對象具備狀態,一個對象用數據值來描述它的狀態。
對象還有操做,用於改變對象的狀態,對象及其操做就是對象的行爲。
對象實現了數據和操做的結合,使數據和操做封裝於對象的統一體中
(3)類。
具備相同或類似性質的對象的抽象就是類。所以,對象的抽象是類,類的具體化就是對象,也能夠說類的實例是對象。
類具備屬性,它是對象的狀態的抽象,用數據結構來描述類的屬性。
類具備操做,它是對象的行爲的抽象,用操做名和實現該操做的方法來描述。
(4)類的結構。
在客觀世界中有若干類,這些類之間有必定的結構關係。一般有兩種主要的結構關係,即通常--具體結構關係,總體--部分結構關係。
①通常——具體結構稱爲分類結構,也能夠說是「或」關係,或者是「is a」關係。
②總體——部分結構稱爲組裝結構,它們之間的關係是一種「與」關係,或者是「has a」關係。
(5)消息和方法。
對象之間進行通訊的結構叫作消息。在對象的操做中,當一個消息發送給某個對象時,消息包含接收對象去執行某種操做的信息。發送一條消息至少要包括說明接受消息的對象名、發送給該對象的消息名(即對象名、方法名)。通常還要對參數加以說明,參數能夠是認識該消息的對象所知道的變量名,或者是全部對象都知道的全局變量名。
類中操做的實現過程叫作方法,一個方法有方法名、參數、方法體。消息傳遞如圖10-1所示。
2、面向對象的特徵
(1)對象惟一性。
每一個對象都有自身惟一的標識,經過這種標識,可找到相應的對象。在對象的整個生命期中,它的標識都不改變,不一樣的對象不能有相同的標識。
(2)分類性。
分類性是指將具備一致的數據結構(屬性)和行爲(操做)的對象抽象成類。一個類就是這樣一種抽象,它反映了與應用有關的重要性質,而忽略其餘一些無關內容。任何類的劃分都是主觀的,但必須與具體的應用有關。
(3)繼承性。
繼承性是子類自動共享父類數據結構和方法的機制,這是類之間的一種關係。在定義和實現一個類的時候,能夠在一個已經存在的類的基礎之上來進行,把這個已經存在的類所定義的內容做爲本身的內容,並加入若干新的內容。
繼承性是面向對象程序設計語言不一樣於其它語言的最重要的特色,是其餘語言所沒有的。
在類層次中,子類只繼承一個父類的數據結構和方法,則稱爲單重繼承。
在類層次中,子類繼承了多個父類的數據結構和方法,則稱爲多重繼承。
在軟件開發中,類的繼承性使所創建的軟件具備開放性、可擴充性,這是信息組織與分類的行之有效的方法,它簡化了對象、類的建立工做量,增長了代碼的可重性。
採用繼承性,提供了類的規範的等級結構。經過類的繼承關係,使公共的特性可以共享,提升了軟件的重用性。
(4)多態性(多形性)
多態性使指相同的操做或函數、過程可做用於多種類型的對象上並得到不一樣的結果。不一樣的對象,收到同一消息能夠產生不一樣的結果,這種現象稱爲多態性。
多態性容許每一個對象以適合自身的方式去響應共同的消息。
多態性加強了軟件的靈活性和重用性。
3、面向對象的要素
(1)抽象。
抽象是指強調實體的本質、內在的屬性。在系統開發中,抽象指的是在決定如何實現對象以前的對象的意義和行爲。使用抽象能夠儘量避免過早考慮一些細節。
類實現了對象的數據(即狀態)和行爲的抽象。
(2)封裝性(信息隱藏)。
封裝性是保證軟件部件具備優良的模塊性的基礎。
面向對象的類是封裝良好的模塊,類定義將其說明(用戶可見的外部接口)與實現(用戶不可見的內部實現)顯式地分開,其內部實現按其具體定義的做用域提供保護。
對象是封裝的最基本單位。封裝防止了程序相互依賴性而帶來的變更影響。面向對象的封裝比傳統語言的封裝更爲清晰、更爲有力。
(3)共享性
面向對象技術在不一樣級別上促進了共享
同一類中的共享。同一類中的對象有着相同數據結構。這些對象之間是結構、行爲特徵的共享關係。
在同一應用中共享。在同一應用的類層次結構中,存在繼承關係的各類似子類中,存在數據結構和行爲的繼承,使各類似子類共享共同的結構和行爲。使用繼承來實現代碼的共享,這也是面向對象的主要優勢之一。
在不一樣應用中共享。面向對象不只容許在同一應用中共享信息,並且爲將來目標的可重用設計準備了條件。經過類庫這種機制和結構來實現不一樣應用中的信息共享。
4.強調對象結構而不是程序結構
4、面向對象的開發方法
目前,面向對象開發方法的研究已日趨成熟,國際上已有很多面向對象產品出現。面向對象開發方法有Coad方法、Booch方法和OMT方法等。
1.Booch方法
Booch最早描述了面向對象的軟件開發方法的基礎問題,指出面向對象開發是一種根本不一樣於傳統的功能分解的設計方法。面向對象的軟件分解更接近人對客觀事務的理解,而功能分解只經過問題空間的轉換來得到。
2.Coad方法
Coad方法是1989年Coad和Yourdon提出的面向對象開發方法。該方法的主要優勢是經過多年來大系統開發的經驗與面向對象概念的有機結合,在對象、結構、屬性和操做的認定方面,提出了一套系統的原則。該方法完成了從需求角度進一步進行類和類層次結構的認定。儘管Coad方法沒有引入類和類層次結構的術語,但事實上已經在分類結構、屬性、操做、消息關聯等概念中體現了類和類層次結構的特徵。
3.OMT方法
OMT方法是1991年由James Rumbaugh等5人提出來的,其經典著做爲「面向對象的建模與設計」。
該方法是一種新興的面向對象的開發方法,開發工做的基礎是對真實世界的對象建模,而後圍繞這些對象使用分析模型來進行獨立於語言的設計,面向對象的建模和設計促進了對需求的理解,有利於開發得更清晰、更容易維護的軟件系統。該方法爲大多數應用領域的軟件開發提供了一種實際的、高效的保證,努力尋求一種問題求解的實際方法。
4.UML(Unified Modeling Language)語言
軟件工程領域在1995年~1997年取得了史無前例的進展,其成果超過軟件工程領域過去15年的成就總和,其中最重要的成果之一就是統一建模語言(UML)的出現。UML將是面向對象技術領域內占主導地位的標準建模語言。UML不只統一了Booch方法、OMT方法、OOSE方法的表示方法,並且對其做了進一步的發展,最終統一爲大衆接受的標準建模語言。UML是一種定義良好、易於表達、功能強大且廣泛適用的建模語言。它融入了軟件工程領域的新思想、新方法和新技術。它的做用域不限於支持面向對象的分析與設計,還支持從需求分析開始的軟件開發全過程。
5、面向對象的模型
·對象模型
對象模型表示了靜態的、結構化的系統數據性質,描述了系統的靜態結構,它是從客觀世界實體的對象關係角度來描述,表現了對象的相互關係。該模型主要關心繫統中對象的結構、屬性和操做,它是分析階段三個模型的核心,是其餘兩個模型的框架。
1.對象和類
(1) 對象。
對象建模的目的就是描述對象。
(2) 類。
經過將對象抽象成類,咱們能夠使問題抽象化,抽象加強了模型的概括能力。
(3) 屬性。
屬性指的是類中對象所具備的性質(數據值)。
(4) 操做和方法。
操做是類中對象所使用的一種功能或變換。類中的各對象能夠共享操做,每一個操做都有一個目標對象做爲其隱含參數。
方法是類的操做的實現步驟。
2.關聯和鏈
關聯是創建類之間關係的一種手段,而鏈則是創建對象之間關係的一種手段。
(1) 關聯和鏈的含義。
鏈表示對象間的物理與概念聯結,關聯表示類之間的一種關係,鏈是關聯的實例,關聯是鏈的抽象。
(2) 角色。
角色說明類在關聯中的做用,它位於關聯的端點。
(3) 受限關聯。
受限關聯由兩個類及一個限定詞組成,限定詞是一種特定的屬性,用來有效的減小關聯的重數,限定詞在關聯的終端對象集中說明。
限定提升了語義的精確性,加強了查詢能力,在現實世界中,經常出現限定詞。
(4) 關聯的多重性。
關聯的多重性是指類中有多少個對象與關聯的類的一個對象相關。重數常描述爲「一」或「多」。
圖10-8表示了各類關聯的重數。小實心圓表示「多個」,從零到多。小空心圓表示零或一。沒有符號表示的是一對一關聯。
3.類的層次結構
(1) 彙集關係。
彙集是一種「總體-部分」關係。在這種關係中,有總體類和部分類之分。彙集最重要的性質是傳遞性,也具備逆對稱性。
彙集能夠有不一樣層次,能夠把不一樣分類彙集起來獲得一顆簡單的彙集樹,彙集樹是一種簡單表示,比畫不少線來將部分類聯繫起來簡單得多,對象模型應該容易地反映各級層次,圖10-10表示一個關於微機的多極彙集。
(2)通常化關係。
通常化關係是在保留對象差別的同時共享對象類似性的一種高度抽象方式。它是「通常---具體」的關係。通常化類稱爲你類,具體類又能稱爲子類,各子類繼承了交類的性質,而各子類的一些共同性質和操做又概括到你類中。所以,通常化關係和繼承是同時存在的。通常化關係的符號表示是在類關聯的連線上加一個小三角形,如圖10-11
4.對象模型
(1)模板。模板是類、關聯、通常化結構的邏輯組成。
(2)對象模型。
對象模型是由一個或若干個模板組成。模板將模型分爲若干個便於管理的子塊,在整個對象模型和類及關聯的構造塊之間,模板提供了一種集成的中間單元,模板中的類名及關聯名是惟一的。
·動態模型
動態模型是與時間和變化有關的系統性質。該模型描述了系統的控制結構,它表示了瞬間的、行爲化的系統控制
性質,它關心的是系統的控制,操做的執行順序,它表示從對象的事件和狀態的角度出發,表現了對象的相互行爲。
該模型描述的系統屬性是觸發事件、事件序列、狀態、事件與狀態的組織。使用狀態圖做爲描述工具。它涉及到事件、狀態、操做等重要概念。
1.事件
事件是指定時刻發生的某件事。
2.狀態
狀態是對象屬性值的抽象。對象的屬性值按照影響對象顯著行爲的性質將其歸併到一個狀態中去。狀態指明瞭對象
對輸入事件的響應。
3.狀態圖
狀態圖是一個標準的計算機概念,他是有限自動機的圖形表示,這裏把狀態圖做爲創建動態模型的圖形工具。
狀態圖反映了狀態與事件的關係。當接收一事件時,下一狀態就取決於當前狀態和所接收的該事件,由該事件引發的狀態變化稱爲轉換。
狀態圖是一種圖,用結點表示狀態,結點用圓圈表示;圓圈內有狀態名,用箭頭連線表示狀態的轉換,上面標記事件名,箭頭方向表示轉換的方向。
·功能模型
功能模型描述了系統的全部計算。功能模型指出發生了什麼,動態模型肯定何時發生,而對象模型肯定發生的客體。功能模型代表一個計算如何從輸入值獲得輸出值,它不考慮計算的次序。功能模型由多張數據流圖組成。數據流圖用來表示從源對象到目標對象的數據值的流向,它不包含控制信息,控制信息在動態模型中表示,同時數據流圖也不表示對象中值的組織,值的組織在對象模型中表示。圖10-15給出了一個窗口系統的圖標顯示的數據流圖。
數據流圖中包含有處理、數據流、動做對象和數據存儲對象。
1.處理
數據流圖中的處理用來改變數據值。最低層處理是純粹的函數,一張完整的數據流圖是一個高層處理。
2.數據流
數據流圖中的數據流將對象的輸出與處理、處理與對象的輸入、處理與處理聯繫起來。在一個計算機中,用數據流來表示一中間數據值,數據流不能改變數據值。
3.動做對象
動做對象是一種主動對象,它經過生成或者使用數據值來驅動數據流圖。
4.數據存儲對象
數據流圖中的數據存儲是被動對象,它用來存儲數據。它與動做對象不同,數據存儲自己不產生任何操做,它只響應存儲和訪問的要求。
6、面向對象的分析
面向對象分析的目的是對客觀世界的系統進行建模。本節以上面介紹的模型概念爲基礎,結合「銀行網絡系統」的具體實例來構造客觀世界問題的準確、嚴密的分析模型。
分析模型有三種用途:用來明確問題需求;爲用戶和開發人員提供明確需求;爲用戶和開發人員提供一個協商的基礎,做爲後繼的設計和實現的框架。
(一) 面向對象的分析
系統分析的第一步是:陳述需求。分析者必須同用戶一塊工做來提煉需求,由於這樣才表示了用戶的真實意圖,其中涉及對需求的分析及查找丟失的信息。下面以「銀行網絡系統」爲例,用面向對象方法進行開發。
銀行網絡系統問題陳述:設計支持銀行網絡的軟件,銀行網絡包括人工出納站和分行共享的自動出納機。每一個分理處用分理處計算機來保存各自的賬戶,處理各自的事務;各自分理處的出納站與分理處計算機通訊,出納站錄入賬戶和事務數據;自動出納機與分行計算機通訊,分行計算機與撥款分理處結賬,自動出納機與用戶接口接受現金卡,與分行計算機通訊完成事務,發放現金,打印收據;系統須要記錄保管和安全措施;系統必須正確處理同一賬戶的併發訪問;每一個分處理爲本身的計算機準備軟件,銀行網絡費用根據顧客和現金卡的數目分攤給各分理處。
圖10-18給出銀行網絡系統的示意圖。
(二)創建對象模型
首先標識和關聯,由於它們影響了總體結構和解決問題的方法,其次是增長屬性,進一步描述類和關聯的基本網絡,使用繼承合併和組織類,最後操做增長到類中去做爲構造動態模型和功能模型的副產品。
1.肯定類
構造對象模型的第一步是標出來自問題域的相關的對象類,對象包括物理實體和概念。全部類在應用中都必須有意義,在問題陳述中,並不是全部類都是明顯給出的。有些是隱含在問題域或通常知識中的。
按圖10-19所示的過程肯定類
查找問題陳述中的全部名詞,產生以下的暫定類。
軟件 銀行網絡 出納員 自動出納機 分行
分處理 分處理計算機 賬戶 事務 出納站
事務數據 分行計算機 現金卡 用戶 現金
收據 系統 顧客 費用 賬戶數據
訪問 安全措施 記錄保管
根據下列標準,去掉沒必要要的類和不正確的類。
(1) 冗餘類:若兩個類表述了同一個信息 ,保留最富有描述能力的類。如"用戶"和"顧客"就是重複的描述,由於"顧客"最富有描述性,所以保留它。
(2) 不相干的類:除掉與問題沒有關係或根本無關的類。例如,攤派費用超出了銀行網絡的範圍。
(3) 模糊類:類必須是肯定的,有些暫定類邊界定義模糊或範圍太廣,如"記錄保管"就模糊類,它是"事務"中的一部分。
(4) 屬性:某些名詞描述的是其餘對象的屬性,則從暫定類中刪除。若是某一性質的獨立性很重要,就應該把他歸屬到類,而不把它做爲屬性。
(5) 操做:若是問題陳述中的名詞有動做含義,則描述的操做就不是類。可是具備自身性質並且須要獨立存在的操做應該描述成類。如咱們只構造電話模型,"撥號"就是動態模型的一部分而不是類,但在電話撥號系統中,"撥號"是一個重要的類,它日期、時間、受話地點等屬性。
在銀行網絡系統中,模糊類是"系統"、"安全措施"、"記錄保管"、"銀行網絡"等。屬於屬性的有:"賬戶數據"、"收據"、"現金"、"事務數據"。屬於實現的如:"訪問"、"軟件"等。這些均應除去。
2.準備數據字典
爲全部建模實體準備一個數據字典。準確描述各個類的精確含義,描述當前問題中的類的範圍,包括對類的成員、用法方面的假設或限制。
3.肯定關聯
兩個或多個類之間的相互依賴就是關聯。一種依賴表示一種關聯,可用各類方式來實現關聯,但在分析模型中應刪除實現的考慮,以便設計時更爲靈活。關聯經常使用描述性動詞或動詞詞組來表示,其中有物理位置的表示、傳導的動做、通訊、全部者關係、條件的知足等。從問題陳述中抽取全部可能的關聯表述,把它們記下來,但不要過早去細化這些表述。
下面是銀行網絡系統中全部可能的關聯,大多數是直接抽取問題中的動詞詞組而獲得的。在陳述中,有些動詞詞組表述的關聯是不明顯的。最後,還有一些關聯與客觀世界或人的假設有關,必須同用戶一塊兒覈實這種關聯,由於這種關聯在問題陳述中找不到。
銀行網絡問題陳述中的關聯:
·銀行網絡包括出納站和自動出納機;
·分行共享自動出納機;
·分理處提供分理處計算機;
·分理處計算機保存賬戶;
·分理處計算機處理賬戶支付事務;
·分理處擁有出納站;
·出納站與分理處計算機通訊;
·出納員爲賬戶錄入事務;
·自動出納機接受現金卡;
·自動出納機與用戶接口;
·自動出納機發放現金;
·自動出納機打印收據;
·系統處理併發訪問;
·分理處提供軟件;
·費用分攤給分理處。
隱含的動詞詞組:
·分行由分理處組成;
·分理處擁有賬戶;
·分行擁有分行計算機;
·系統提供記錄保管;
·系統提供安全;
·顧客有現金卡。
基於問題域知識的關聯:
·分理處僱傭出納員;
·現金卡訪問賬戶。
使用下列標準去掉沒必要要和不正確的關聯:
(1) 若某個類已被刪除,那麼與它有關的關聯也必須刪除或者用其它類來從新表述。在例中,咱們刪除了"銀行網絡",相關的關聯也要刪除。
(2) 不相干的關聯或實現階段的關聯:刪除全部問題域以外的關聯或涉及實現結構中的關聯。如"系統處理併發訪問"就是一種實現的概念。
(3) 動做:關聯應該描述應用域的結構性質而不是瞬時事件,所以應刪除"自動出納機接受現金卡","自動出納機與用戶接口"等。
(4) 派生關聯:省略那些能夠用其餘關聯來定義的關聯。由於這種關聯是冗餘的。銀行網絡系統的初步對象圖如圖10-20所示。其中含有關聯。
4.肯定屬性
屬性是個體對象的性質,屬性一般用修飾性的名詞詞組來表示.形容詞經常表示具體的可枚舉的屬性值,屬性不可能在問題陳述中徹底表述出來,必須藉助於應用域的知識及對客觀世界的知識才能夠找到它們。只考慮與具體應用直接相關的屬性,不要考慮那些超出問題範圍的屬性。首先找出重要屬性,避免那些只用於實現的屬性,要爲各個屬性取有意義的名字。按下列標準刪除沒必要要的和不正確的屬性:
(1) 對象:若實體的獨立存在比它的值重要,那麼這個實體不是屬性而是對象。如在郵政目錄中,"城市"是一個屬性,然而在人口普查中,"城市"則被看做是對象。在具體應用中,具備自身性質的實體必定是對象。
(2) 定詞:若屬性值取決於某種具體上下文,則可考慮把該屬性從新表述爲一個限定詞。
(3) 名稱:名稱經常做爲限定詞而不是對象的屬性,當名稱不依賴於上下文關係時,名稱即爲一個對象屬性,尤爲是它不唯一時。
(4) 標識符:在考慮對象模糊性時,引入對象標識符表示,在對象模型中不列出這些對象標識符,它是隱含在對象模型中,只列出存在於應用域的屬性。
(5) 內部值:若屬性描述了對外不透明的對象的內部狀態,則應從對象模型中刪除該屬性。
(6) 細化:忽略那些不可能對大多數操做有影響的屬性。
5.使用繼承來細化類
使用繼承來共享公共機構,以次來組織類,能夠用兩種方式來進行。
(1)自底向上經過把現有類的共同性質通常化爲父類,尋找具備類似的屬性,關係或操做的類來發現繼承。例如"遠程事務"和"出納事務"是相似的,能夠通常化爲" 事務"。有些通常化結構經常是基於客觀世界邊界的現有分類,只要可能,儘可能使用現有概念。對稱性常有助於發現某些丟失的類。
(2)自頂向下將現有的類細化爲更具體的子類。具體化經常能夠從應用域中明顯看出來。應用域中各枚舉字狀況是最多見的具體化的來源。例如:菜單,能夠有固定菜單,頂部菜單,彈出菜單,下拉菜單等,這就能夠把菜單類具體細化爲各類具體菜單的子類。當同一關聯名出現屢次且意義也相同時,應儘可能具體化爲相關聯的類,例如"事務"從"出納站"和"自動出納機"進入,則"錄入站"就是"出納站"和"自動出納站"的通常化。在類層次中,能夠爲具體的類分配屬性和關聯。各屬性和都應分配給最通常的適合的類,有時也加上一些修正。
應用域中各枚舉狀況是最多見的具體化的來源。
6.完善對象模型
對象建模不可能一次就能保證模型是徹底正確的,軟件開發的整個過程就是一個不斷完善的過程。模型的不一樣組成部分多半是在不一樣的階段完成的,若是發現模型的缺陷,就必須返回到前期階段去修改,有些細化工做是在動態模型和功能模型完成以後纔開始進行的。
(1) 幾種可能丟失對象的狀況及解決辦法:
·同一類中存在毫無關係的屬性和操做,則分解這個類,使各部分相互關聯;
·通常化體系不清楚,則可能分離扮演兩種角色的類
·存在無目標類的操做,則找出並加上失去目標的類;
·存在名稱及目的相同的冗餘關聯,則經過通常化建立丟失的父類,把關聯組織在一塊兒。
(2) 查找多餘的類。
類中缺乏屬性,操做和關聯,則可刪除這個類。
(3)查找丟失的關聯。
丟失了操做的訪問路徑,則加入新的關聯以回答查詢。
(4) 網絡系統的具體狀況做以下的修改:
①現金卡有多個獨立的特性。把它分解爲兩個對象:卡片權限和現金卡。
a.卡片權限:它是銀行用來鑑別用戶訪問權限的卡片,表示一個或多個用戶賬戶的訪問權限;各個卡片權限對象中可能具備好幾個現金卡,每張都帶有安全碼,卡片碼,它們附在現金卡上,表現銀行的卡片權限。
b.現金卡:它是自動出納機獲得表示碼的數據卡片,它也是銀行代碼和現金卡代碼的數據載體。
②"事務"不能體現對賬戶之間的傳輸描述的通常性,因它只涉及一個賬戶,通常來講,在每一個賬戶中,一個"事務"包括一個或多個"更新",一個"更新"是對賬戶的一個動做,它們是取款,存款,查詢之一。一個"更新"中全部"更新"應該是一個原子操做。
③"分理處"和"分離處理機"之間,"分行"和"分行處理機"之間的區別彷佛並不影響分析,計算機的通訊處理其實是實現的概念,將"分理處計算機"併入到"分理處",將"分行計算機"併入到"分行"。
(三)創建動態模型
1.準備腳本
動態分析從尋找事件開始,而後肯定各對象的可能事件順序。在分析階段不考慮算法的執行,算法是實現模型的一部分。
2.肯定事件
肯定全部外部事件。事件包括全部來自或發往用戶的信息、外部設備的信號、輸入、轉換和動做,能夠發現正常事件,但不能遺漏條件和異常事件。
3.準備事件跟蹤表
把腳本表示成一個事件跟蹤表,即不一樣對象之間的事件排序表,對象爲表中的列,給每一個對象分配一個獨立的列。
4.構造狀態圖
對各對象類創建狀態圖,反映對象接收和發送的事件,每一個事件跟蹤都對應於狀態圖中一條路徑。
(四)創建功能建模
功能模型用來講明值是如何計算的,代表值之間的依賴關係及相關的功能,數據流圖有助於表示功能依賴關係,其中的處理應於狀態圖的活動和動做,其中的數據流對應於對象圖中的對象或屬性。
1.肯定輸入值、輸出值
先列出輸入、輸出值,輸入、輸出值是系統與外界之間的事件的參數。
2.創建數據流圖
數據流圖說明輸出值是怎樣從輸入值得來的,數據流圖一般按層次組織。
(五)肯定操做
在創建對象模型時,肯定了類、關聯、結構和屬性,尚未肯定操做。只有創建了動態模型和功能模型以後,纔可能最後肯定類的操做。
7、面向對象的設計
面向對象設計是把分析階段獲得的需求轉變成符合成本和質量要求的、抽象的系統實現方案的過程。從面向對象分析到面向對象設計,是一個逐漸擴充模型的過程。
瀑布模型把設計進一步劃分紅概要設計和詳細設計兩個階段,相似地,也能夠把面向對象設計再細分爲系統設計和對象設計。系統設計肯定實現系統的策略和目標系統的高層結構。對象設計肯定解空間中的類、關聯、接口形式及實現操做的算法。
(一)面向對象設計的準則
1.模塊化
面向對象開發方法很天然地支持了把系統分解成模塊的設計原則:對象就是模塊。它是把數據結構和操做這些數據的方法緊密地結合在一塊兒所構成的模塊。
2.抽象
面向對象方法不只支持過程抽象,並且支持數據抽象。
3.信息隱藏
在面向對象方法中,信息隱藏經過對象的封裝性來實現。
4.低耦合
在面向對象方法中,對象是最基本的模塊,所以,耦合主要指不一樣對象之間相互關聯的緊密程度。低耦合是設計的一個重要標準,由於這有助於使得系統中某一部分的變化對其它部分的影響降到最低程度。
5.高內聚
(1)操做內聚。
(2)類內聚。
(3)通常——具體內聚。
(二)面向對象設計的啓發規則
1.設計結果應該清晰易懂
使設計結果清晰、易懂、易讀是提升軟件可維護性和可重用性的重要措施。顯然,人們不會重用那些他們不理解的設計。
要作到:
(1)用詞一致。
(2)使用已有的協議。
(3)減小消息模式的數量。
(4)避免模糊的定義。
2.通常——具體結構的深度應適當
3.設計簡單類
應該儘可能設計小而簡單的類,這樣便以開發和管理。爲了保持簡單,應注意如下幾點:
(1)避免包含過多的屬性。
(2)有明確的定義。
(3)儘可能簡化對象之間的合做關係。
(4)不要提供太多的操做。
4.使用簡單的協議
通常來講,消息中參數不要超過3個。
5.使用簡單的操做
面向對象設計出來的類中的操做一般都很小,通常只有3至5行源程序語句,能夠用僅含一個動詞和一個賓語的簡單句子描述它的功能
6.把設計變更減至最小
一般,設計的質量越高,設計結果保持不變的時間也越長。即便出現必須修改設計的狀況,也應該使修改的範圍儘量小。
(三)系統設計
系統設計是問題求解及創建解答的高級策略。必須制定解決問題的基本方法,系統的高層結構形式包括子系統的分解、它的固有併發性、子系統分配給硬軟件、數據存儲管理、資源協調、軟件控制實現、人機交互接口。
1.系統設計概述
設計階段先從高層入手,而後細化。系統設計要決定整個結構及風格,這種結構爲後面設計階段的更詳細策略的設計提供了基礎。
(1)系統分解。
系統中主要的組成部分稱爲子系統,子系統既不是一個對象也不是一個功能,而是類、關聯、操做、事件和約束的集合。
(2)肯定併發性。
分析模型、現實世界及硬件中很多對象均是併發的。
(3)處理器及任務分配。
各併發子系統必須分配給單個硬件單元,要麼是一個通常的處理器,要麼是一個具體的功能單元。
(4)數據存儲管理。
系統中的內部數據和外部數據的存儲管理是一項重要的任務。一般各數據存儲能夠將數據結構、文件、數據庫組合在一塊兒,不一樣數據存儲要在費用、訪問時間、容量及可靠性之間作出折衷考慮。
(5)全局資源的處理。
必須肯定全局資源,而且制定訪問全局資源的策略。
(6)選擇軟件控制機制。
分析模型中全部交互行爲都表示爲對象之間的事件。系統設計必須從多種方法中選擇某種方法來實現軟件的控制。
(7)人機交互接口設計。
設計中的大部分工做都與穩定的狀態行爲有關,但必須考慮用戶使用系統的交互接口。
2.系統結構的通常框架
3.系統分解——創建系統的體系結構
可用的軟件庫以及程序員的編程經驗。
經過面向對象分析獲得的問題域精確模型,爲設計體系結構奠基了良好的基礎,創建了完整的框架。
4.選擇軟件控制機制
軟件系統中存在兩種控制流,外部控制流和內部控制流。
5.數據存儲管理
數據存儲管理是系統存儲或檢索對象的基本設施,它創建在某種數據存儲管理系統之上,而且隔離了數據存儲管理模式的影響。
6.設計人機交互接口
在面向對象分析過程當中,已經對用戶界面需求做了初步分析,在面向對象設計過程當中,則應該對系統的人機交互接口進行詳細設計,以肯定人機交互的細節,其中包括指定窗口和報表的形式、設計命令層次等項內容。
(四)對象設計
1.對象設計概述
2.三種模型的結合
(1)得到操做。
(2)肯定操做的目標對象。
3.算法設計
4.優化設計
5.控制的實現
6.調整繼承
7.關聯的設計
8、面向對象的實現
(一)程序設計語言
1.選擇面嚮對象語言
採用面向對象方法開發軟件的基本目的和主要優勢是經過重用提升軟件的生產率。所以,應該優先選用可以最完善、最準確地表達問題域語義的面嚮對象語言。
在選擇編程語言時,應該考慮的其餘因素還有:對用戶學習面向對象分析、設計和編碼技術所能提供的培訓操做;在使用這個面嚮對象語言期間能提供的技術支持;能提供給開發人員使用的開發工具、開發平臺,對機器性能和內存的需求,集成已有軟件的容易程度。
2.程序設計風格
(1)提升重用性。
(2)提升可擴充性。
(3)提升健壯性。
(二)類的實現
在開發過程當中,類的實現是核心問題。在用面向對象風格所寫的系統中,全部的數據都被封裝在類的實例中。而整個程序則被封裝在一個更高級的類中。在使用既存部件的面向對象系統中,能夠只花費少許時間和工做量來實現軟件。只要增長類的實例,開發少許的新類和實現各個對象之間互相通訊的操做,就能創建須要的軟件。
一種方案是先開發一個比較小、比較簡單的來,做爲開發比較大、比較複雜的類的基礎。
(1)「原封不動」重用。
(2)進化性重用。
一個可以徹底符合要求特性的類可能並不存在。
(3)「廢棄性」開發。
不用任何重用來開發一個新類。
(4)錯誤處理。
一個類應是自主的,有責任定位和報告錯誤。
(三)應用系統的實現
應用系統的實現是在全部的類都被實現以後的事。實現一個系統是一個比用過程性方法更簡單、更簡短的過程。有些實例將在其餘類的初始化過程當中使用。而其他的則必須用某種主過程顯式地加以說明,或者看成系統最高層的類的表示的一部分。
在C++和C中有一個main( )函數,能夠使用這個過程來講明構成系統主要對象的那些類的實例。
(四)面向對象測試
(1)算法層。
(2)類層。
測試封裝在同一個類中的全部方法和屬性之間的相互做用。
(3)模板層。
測試一組協同工做的類之間的相互做用。
(4)系統層。
把各個子系統組裝成完整的面向對象軟件系統,在組裝過程當中同時進行測試。
9、面向對象和基於對象的區別
不少人沒有區分「面向對象」和「基於對象」兩個不一樣的概念。面向對象的三大特色(封裝,繼承,多態)卻一不可。一般「基於對象」是使用對象,可是沒法利用現有的對象模板產生新的對象類型,繼而產生新的對象,也就是說「基於對象」沒有繼承的特色。而「多態」表示爲父類類型的子類對象實例,沒有了繼承的概念也就無從談論「多態」。如今的不少流行技術都是基於對象的,它們使用一些封裝好的對象,調用對象的方法,設置對象的屬性。可是它們沒法讓程序員派生新對象類型。他們只能使用現有對象的方法和屬性。因此當你判斷一個新的技術是不是面向對象的時候,一般能夠使用後兩個特性來加以判斷。「面向對象」和「基於對象」 都實現了「封裝」的概念,可是面向對象實現了「繼承和多態」,而「基於對象」沒有實現這些,的確很饒口。
從事面向對象編程的人按照分工來講,能夠分爲「類庫的建立者」和「類庫的使用者」。使用類庫的人並不都是具有了面向對象思想的人,一般知道如何繼承和派生新對象就能夠使用類庫了,然而咱們的思惟並無真正的轉過來,使用類庫只是在形式上是面向對象,而實質上只是庫函數的一種擴展。
面向對象是一種思想,是咱們考慮事情的方法,一般表現爲咱們是將問題的解決按照過程方式來解決呢,仍是將問題抽象爲一個對象來解決它。不少狀況下,咱們會不知不覺的按照過程方式來解決它,而不是考慮將要解決問題抽象爲對象去解決它。有些人打着面向對象的幌子,幹着過程編程的勾當。
耦合與變化:
耦合是軟件不能抵禦變化災難的根本性緣由。不只實體對象與實體對象之間存在耦合關係,實體對象與行爲操做之間也存在耦合關係。
動機(Motivate):
在軟件系統中,「行爲請求者」與「行爲實現者」一般呈現一種「緊耦合」。但在某些場合,好比要對行爲進行「記錄、撤銷/重作、事務」等處理,這種沒法抵禦變化的緊耦合是不合適的。
在這種狀況下,如何將「行爲請求者」與「行爲實現者」解耦?將一組行爲抽象爲對象,能夠實現兩者之間的鬆耦合。
意圖(Intent):
將一個請求封裝爲一個對象,從而使你可用不一樣的請求對客戶進行參數化;對請求排隊或記錄請求日誌,以及支持可撤消的操做。
-------《設計模式》GOF
結構圖(Struct):
適用性:
1.使用命令模式做爲"CallBack"在面向對象系統中的替代。"CallBack"講的即是先將一個函數登記上,而後在之後調用此函數。
2.須要在不一樣的時間指定請求、將請求排隊。一個命令對象和原先的請求發出者能夠有不一樣的生命期。換言之,原先的請求發出者可能已經不在了,而命令對象自己仍然是活動的。這時命令的接收者能夠是在本地,也能夠在網絡的另一個地址。命令對象能夠在串形化以後傳送到另一臺機器上去。
3.系統須要支持命令的撤消(undo)。命令對象能夠把狀態存儲起來,等到客戶端須要撤銷命令所產生的效果時,能夠調用undo()方法,把命令所產生的效果撤銷掉。命令對象還能夠提供redo()方法,以供客戶端在須要時,再從新實施命令效果。
4.若是一個系統要將系統中全部的數據更新到日誌裏,以便在系統崩潰時,能夠根據日誌裏讀回全部的數據更新命令,從新調用Execute()方法一條一條執行這些命令,從而恢復系統在崩潰前所作的數據更新。
生活中的例子:
Command模式將一個請求封裝爲一個對象,從而使你能夠使用不一樣的請求對客戶進行參數化。用餐時的帳單是Command模式的一個例子。服務員接受顧客的點單,把它記在帳單上封裝。這個點單被排隊等待烹飪。注意這裏的"帳單"是不依賴於菜單的,它能夠被不一樣的顧客使用,所以它能夠添入不一樣的點單項目。
代碼實現:
在衆多的設計模式中,Command模式是很簡單也很優雅的一種設計模式。Command模式它封裝的是命令,把命令發出者的責任和命令執行者的責任分開。咱們知道,一個類是一組操做和相應的一些變量的集合,如今有這樣一個類Document,以下:
1 /// <summary>
2
3 /// 文檔類
4
5 /// </summary>
6
7 public class Document
8
9 {
10 /**//// <summary>
11
12 /// 顯示操做
13
14 /// </summary>
15
16 public void Display()
17
18 {
19 Console.WriteLine("Display");
20 }
21
22 /**//// <summary>
23
24 /// 撤銷操做
25
26 /// </summary>
27
28 public void Undo()
29
30 {
31 Console.WriteLine("Undo");
32 }
33
34 /**//// <summary>
35
36 /// 恢復操做
37
38 /// </summary>
39
40 public void Redo()
41
42 {
43 Console.WriteLine("Redo");
44 }
45 }
一般客戶端實現代碼以下:
1 class Program
2
3 {
4 static void Main(string[] args)
5
6 {
7 Document doc = new Document();
8
9 doc.Display();
10
11 doc.Undo();
12
13 doc.Redo();
14 }
15 }
這樣的使用原本是沒有任何問題的,可是咱們看到在這個特定的應用中,出現了Undo/Redo的操做,這時若是行爲的請求者和行爲的實現者之間仍是呈現這樣一種緊耦合,就不太合適了。能夠看到,客戶程序是依賴於具體Document的命令(方法)的,引入Command模式,須要對Document中的三個命令進行抽象,這是Command模式最有意思的地方,由於在咱們看來Display(),Undo(),Redo()這三個方法都應該是Document所具備的,若是單獨抽象出來成一個命令對象,那就是把函數層面的功能提到了類的層面,有點功能分解的味道,我以爲這正是Command模式解決這類問題的優雅之處,先對命令對象進行抽象:
1 /// <summary>
2
3 /// 抽象命令
4
5 /// </summary>
6
7 public abstract class DocumentCommand
8
9 {
10 Document _document;
11
12 public DocumentCommand(Document doc)
13
14 {
15 this._document = doc;
16 }
17
18 /**//// <summary>
19
20 /// 執行
21
22 /// </summary>
23
24 public abstract void Execute();
25
26 }
其餘的具體命令類都繼承於該抽象類,以下:
示意性代碼以下:
1 /// <summary>
2
3 /// 顯示命令
4
5 /// </summary>
6
7 public class DisplayCommand : DocumentCommand
8
9 {
10 public DisplayCommand(Document doc)
11
12 : base(doc)
13 {
14
15 }
16
17 public override void Execute()
18
19 {
20 _document.Display();
21 }
22 }
23
24
25 /**//// <summary>
26
27 /// 撤銷命令
28
29 /// </summary>
30
31 public class UndoCommand : DocumentCommand
32
33 {
34 public UndoCommand(Document doc)
35
36 : base(doc)
37 {
38
39 }
40
41 public override void Execute()
42
43 {
44 _document.Undo();
45 }
46 }
47
48
49 /**//// <summary>
50
51 /// 重作命令
52
53 /// </summary>
54
55 public class RedoCommand : DocumentCommand
56
57 {
58 public RedoCommand(Document doc)
59
60 : base(doc)
61 {
62
63 }
64
65 public override void Execute()
66
67 {
68 _document.Redo();
69 }
70 }
如今還須要一個Invoker角色的類,這其實至關於一箇中間角色,前面我曾經說過,使用這樣的一箇中間層也是咱們常用的手法,即把A對B的依賴轉換爲A對C的依賴。以下:
1 /// <summary>
2
3 /// Invoker角色
4
5 /// </summary>
6
7 public class DocumentInvoker
8
9 {
10 DocumentCommand _discmd;
11
12 DocumentCommand _undcmd;
13
14 DocumentCommand _redcmd;
15
16 public DocumentInvoker(DocumentCommand discmd,DocumentCommand undcmd,DocumentCommand redcmd)
17 {
18
19 this._discmd = discmd;
20
21 this._undcmd = undcmd;
22
23 this._redcmd = redcmd;
24
25 }
26
27 public void Display()
28
29 {
30 _discmd.Execute();
31 }
32
33 public void Undo()
34
35 {
36 _undcmd.Execute();
37 }
38
39 public void Redo()
40
41 {
42 _redcmd.Execute();
43 }
44 }
45
46 如今再來看客戶程序的調用代碼:
47 class Program
48
49 {
50 static void Main(string[] args)
51
52 {
53
54 Document doc = new Document();
55
56
57 DocumentCommand discmd = new DisplayCommand(doc);
58
59 DocumentCommand undcmd = new UndoCommand(doc);
60
61 DocumentCommand redcmd = new RedoCommand(doc);
62
63
64 DocumentInvoker invoker = new DocumentInvoker(discmd,undcmd,redcmd);
65
66 invoker.Display();
67
68 invoker.Undo();
69
70 invoker.Redo();
71
72 }
73 }
能夠看到在客戶程序中,再也不依賴於Document的Display(),Undo(),Redo()命令,經過Command對這些命令進行了封裝,使用它的一個關鍵就是抽象的Command類,它定義了一個操做的接口。同時咱們也能夠看到,原本這三個命令僅僅是三個方法而已,可是經過Command模式卻把它們提到了類的層面,這實際上是違背了面向對象的原則,但它卻優雅的解決了分離命令的請求者和命令的執行者的問題,在使用Command模式的時候,必定要判斷好使用它的時機。
Command實現要點:
1.Command模式的根本目的在於將「行爲請求者」與「行爲實現者」解耦,在面嚮對象語言中,常見的實現手段是「將行爲抽象爲對象」。
2.實現Command接口的具體命令對象ConcreteCommand有時候根據須要可能會保存一些額外的狀態信息。
3.經過使用Compmosite模式,能夠將多個命令封裝爲一個「複合命令」MacroCommand。
4.Command模式與C#中的Delegate有些相似。但二者定義行爲接口的規範有所區別:Command以面向對象中的「接口-實現」來定義行爲接口規範,更嚴格,更符合抽象原則;Delegate以函數簽名來定義行爲接口規範,更靈活,但抽象能力比較弱。
5.使用命令模式會致使某些系統有過多的具體命令類。某些系統可能須要幾十個,幾百個甚至幾千個具體命令類,這會使命令模式在這樣的系統裏變得不實際。
Command的優缺點:
命令容許請求的一方和接收請求的一方可以獨立演化,從並且有如下的優勢:
1.命令模式使新的命令很容易地被加入到系統裏。
2.容許接收請求的一方決定是否要否決(Veto)請求。
3.能較容易地設計-個命令隊列。
4.能夠容易地實現對請求的Undo和Redo。
5.在須要的狀況下,能夠較容易地將命令記入日誌。
6.命令模式把請求一個操做的對象與知道怎麼執行一個操做的對象分割開。
7.命令類與其餘任何別的類同樣,能夠修改和推廣。
8.你能夠把命令對象聚合在一塊兒,合成爲合成命令。好比宏命令即是合成命令的例子。合成命令是合成模式的應用。
9.因爲加進新的具體命令類不影響其餘的類,所以增長新的具體命令類很容易。
命令模式的缺點以下:
1.使用命令模式會致使某些系統有過多的具體命令類。某些系統可能須要幾十個,幾百個甚至幾千個具體命令類,這會使命令模式在這樣的系統裏變得不實際。
1 class Program
2
3 {
4 static void Main(string[] args)
5
6 {
7 Document doc = new Document();
8
9 doc.Display();
10
11 doc.Undo();
12
13 doc.Redo();
14 }
15 }
動機(Motivate):
在軟件構建過程當中,集合對象內部結構經常變化各異。但對於這些集合對象,咱們但願在不暴露其內部結構的同時,可讓外部客戶代碼透明地訪問其中包含的元素;同時這種「透明遍歷」也爲「 同一種算法在多種集合對象上進行操做」提供了可能。
使用面向對象技術將這種遍歷機制抽象爲「迭代器對象」爲「應對變化中的集合對象」提供了一種優雅的方法。
意圖(Intent):
提供一種方法順序訪問一個聚合對象中各個元素, 而又不需暴露該對象的內部表示。-------《設計模式》GOF
結構圖(Struct):
適用性:
1.訪問一個聚合對象的內容而無需暴露它的內部表示。
2.支持對聚合對象的多種遍歷。
3.爲遍歷不一樣的聚合結構提供一個統一的接口(即, 支持多態迭代)。
生活中的例子:
迭代器提供一種方法順序訪問一個集合對象中各個元素,而又不須要暴露該對象的內部表示。在早期的電視機中,一個撥盤用來改變頻道。當改變頻道時,須要手工轉動撥盤移過每個頻道,而不論這個頻道是否有信號。如今的電視機,使用[後一個]和[前一個]按鈕。當按下[後一個]按鈕時,將切換到下一個預置的頻道。想象一下在陌生的城市中的旅店中看電視。當改變頻道時,重要的不是幾頻道,而是節目內容。若是對一個頻道的節目不感興趣,那麼能夠換下一個頻道,而不須要知道它是幾頻道。
代碼實現:
在面向對象的軟件設計中,咱們常常會遇到一類集合對象,這類集合對象的內部結構可能有着各類各樣的實現,可是歸結起來,無非有兩點是須要咱們去關心的:一是集合內部的數據存儲結構,二是遍歷集合內部的數據。面向對象設計原則中有一條是類的單一職責原則,因此咱們要儘量的去分解這些職責,用不一樣的類去承擔不一樣的職責。Iterator模式就是分離了集合對象的遍歷行爲,抽象出一個迭代器類來負責,這樣既能夠作到不暴露集合的內部結構,又可以讓外部代碼透明的訪問集合內部的數據。下面看一個簡單的示意性例子,類結構圖以下:
首先有一個抽象的彙集,所謂的彙集就是就是數據的集合,能夠循環去訪問它。它只有一個方法GetIterator()讓子類去實現,用來得到一個迭代器對象。
1 /// <summary>
2
3 /// 抽象彙集
4
5 /// </summary>
6
7 public interface IList
8
9 {
10 IIterator GetIterator();
11 }
抽象的迭代器,它是用來訪問彙集的類,封裝了一些方法,用來把彙集中的數據按順序讀取出來。一般會有MoveNext()、CurrentItem()、Fisrt()、Next()等幾個方法讓子類去實現。
1 /// <summary>
2
3 /// 抽象迭代器
4
5 /// </summary>
6
7 public interface IIterator
8 {
9 bool MoveNext();
10
11 Object CurrentItem();
12
13 void First();
14
15 void Next();
16 }
具體的彙集,它實現了抽象彙集中的惟一的方法,同時在裏面保存了一組數據,這裏咱們加上Length屬性和GetElement()方法是爲了便於訪問彙集中的數據。
1 /// <summary>
2
3 /// 具體彙集
4
5 /// </summary>
6
7 public class ConcreteList : IList
8 {
9 int[] list;
10
11 public ConcreteList()
12
13 {
14 list = new int[] { 1,2,3,4,5};
15 }
16
17 public IIterator GetIterator()
18
19 {
20 return new ConcreteIterator(this);
21 }
22
23 public int Length
24
25 {
26 get { return list.Length; }
27 }
28
29 public int GetElement(int index)
30
31 {
32 return list[index];
33 }
34 }
具體迭代器,實現了抽象迭代器中的四個方法,在它的構造函數中須要接受一個具體彙集類型的參數,在這裏面咱們能夠根據實際的狀況去編寫不一樣的迭代方式。
1 /**//// <summary>
2
3 /// 具體迭代器
4
5 /// </summary>
6
7 public class ConcreteIterator : IIterator
8
9 {
10 private ConcreteList list;
11
12 private int index;
13
14 public ConcreteIterator(ConcreteList list)
15
16 {
17 this.list = list;
18
19 index = 0;
20 }
21
22 public bool MoveNext()
23
24 {
25 if (index < list.Length)
26
27 return true;
28
29 else
30
31 return false;
32 }
33
34 public Object CurrentItem()
35
36 {
37 return list.GetElement(index) ;
38 }
39
40 public void First()
41
42 {
43 index = 0;
44 }
45
46 public void Next()
47
48 {
49 if (index < list.Length)
50
51 {
52 index++;
53 }
54 }
55 }
簡單的客戶端程序調用:
1 /**//// <summary>
2
3 /// 客戶端程序
4
5 /// </summary>
6
7 class Program
8
9 {
10 static void Main(string[] args)
11
12 {
13 IIterator iterator;
14
15 IList list = new ConcreteList();
16
17 iterator = list.GetIterator();
18
19 while (iterator.MoveNext())
20
21 {
22 int i = (int)iterator.CurrentItem();
23 Console.WriteLine(i.ToString());
24
25 iterator.Next();
26 }
27
28 Console.Read();
29
30 }
31
32 }
.NET中Iterator中的應用:
在.NET下實現Iterator模式,對於彙集接口和迭代器接口已經存在了,其中IEnumerator扮演的就是迭代器的角色,它的實現以下:
1 public interface IEumerator
2
3 {
4 object Current
5 {
6 get;
7 }
8
9 bool MoveNext();
10
11 void Reset();
12
13 }
屬性Current返回當前集合中的元素,Reset()方法恢復初始化指向的位置,MoveNext()方法返回值true表示迭代器成功前進到集合中的下一個元素,返回值false表示已經位於集合的末尾。可以提供元素遍歷的集合對象,在.Net中都實現了IEnumerator接口。
IEnumerable則扮演的就是抽象彙集的角色,只有一個GetEnumerator()方法,若是集合對象須要具有跌代遍歷的功能,就必須實現該接口。
1 public interface IEnumerable
2
3 {
4 IEumerator GetEnumerator();
5 }
Iterator實現要點:
1.迭代抽象:訪問一個聚合對象的內容而無需暴露它的內部表示。
2.迭代多態:爲遍歷不一樣的集合結構提供一個統一的接口,從而支持一樣的算法在不一樣的集合結構上進行操做。
3.迭代器的健壯性考慮:遍歷的同時更改迭代器所在的集合結構,會致使問題。
Shift+Alt+Enter: 切換全屏編輯
Ctrl+B,T / Ctrl+K,K: 切換書籤開關
Ctrl+B,N / Ctrl+K,N: 移動到下一書籤
Ctrl+B,P: 移動到上一書籤
Ctrl+B,C: 清除所有標籤
Ctrl+I: 漸進式搜索
Ctrl+Shift+I: 反向漸進式搜索
Ctrl+F: 查找
Ctrl+Shift+F: 在文件中查找
F3: 查找下一個
Shift+F3: 查找上一個
Ctrl+H: 替換
Ctrl+Shift+H: 在文件中替換
Alt+F12: 查找符號(列出全部查找結果)
Ctrl+Shift+V: 剪貼板循環
Ctrl+左右箭頭鍵: 一次能夠移動一個單詞
Ctrl+上下箭頭鍵: 滾動代碼屏幕,但不移動光標位置。
Ctrl+Shift+L: 刪除當前行
Ctrl+M,M: 隱藏或展開當前嵌套的摺疊狀態
Ctrl+M,L: 將全部過程設置爲相同的隱藏或展開狀態
Ctrl+M,P: 中止大綱顯示
Ctrl+E,S: 查看空白
Ctrl+E,W: 自動換行
Ctrl+G: 轉到指定行
Shift+Alt+箭頭鍵: 選擇矩形文本
Alt+鼠標左按鈕: 選擇矩形文本
Ctrl+Shift+U: 所有變爲大寫
Ctrl+U: 所有變爲小寫
代碼快捷鍵
Ctrl+J / Ctrl+K,L: 列出成員
Ctrl+Shift+空格鍵 / Ctrl+K,P: 參數信息
Ctrl+K,I: 快速信息
Ctrl+E,C / Ctrl+K,C: 註釋選定內容
Ctrl+E,U / Ctrl+K,U: 取消選定註釋內容
Ctrl+K,M: 生成方法存根
Ctrl+K,X: 插入代碼段
Ctrl+K,S: 插入外側代碼
F12: 轉到所調用過程或變量的定義
窗口快捷鍵
Ctrl+W,W: 瀏覽器窗口
Ctrl+W,S: 解決方案管理器
Ctrl+W,C: 類視圖
Ctrl+W,E: 錯誤列表
Ctrl+W,O: 輸出視圖
trl+W,P: 屬性窗口
Ctrl+W,T: 任務列表
Ctrl+W,X: 工具箱
Ctrl+W,B: 書籤窗口
Ctrl+W,U: 文檔大綱
Ctrl+D,B: 斷點窗口
Ctrl+D,I: 即時窗口
Ctrl+Tab: 活動窗體切換
Ctrl+Shift+N: 新建項目
Ctrl+Shift+O: 打開項目
Ctrl+Shift+S: 所有保存
Shift+Alt+C: 新建類
Ctrl+Shift+A: 新建項
Shift+Alt+Enter: 切換全屏編輯
Ctrl+B,T / Ctrl+K,K: 切換書籤開關
Ctrl+B,N / Ctrl+K,N: 移動到下一書籤
Ctrl+B,P: 移動到上一書籤
Ctrl+B,C: 清除所有標籤
Ctrl+I: 漸進式搜索
Ctrl+Shift+I: 反向漸進式搜索
Ctrl+F: 查找
Ctrl+Shift+F: 在文件中查找
F3: 查找下一個
Shift+F3: 查找上一個
Ctrl+H: 替換
Ctrl+Shift+H: 在文件中替換
Alt+F12: 查找符號(列出全部查找結果)
Ctrl+Shift+V: 剪貼板循環
Ctrl+左右箭頭鍵: 一次能夠移動一個單詞
Ctrl+上下箭頭鍵: 滾動代碼屏幕,但不移動光標位置。
Ctrl+Shift+L: 刪除當前行
Ctrl+M,M: 隱藏或展開當前嵌套的摺疊狀態
Ctrl+M,L: 將全部過程設置爲相同的隱藏或展開狀態
Ctrl+M,P: 中止大綱顯示
Ctrl+E,S: 查看空白
Ctrl+E,W: 自動換行
Ctrl+G: 轉到指定行
Shift+Alt+箭頭鍵: 選擇矩形文本
Alt+鼠標左按鈕: 選擇矩形文本
Ctrl+Shift+U: 所有變爲大寫
Ctrl+U: 所有變爲小寫
動機(Motivate):
在軟件構建過程當中,咱們須要爲某些對象創建一種「通知依賴關係」 --------一個對象(目標對象)的狀態發生改變,全部的依賴對象(觀察者對象)都將獲得通知。若是這樣的依賴關係過於緊密,將使軟件不能很好地抵禦變化。使用面向對象技術,能夠將這種依賴關係弱化,並造成一種穩定的依賴關係。從而實現軟件體系結構的鬆耦合。
意圖(Intent):
定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時, 全部依賴於它的對象都獲得通知並被自動更新。
-------《設計模式》GOF
結構圖(Struct):
適用性:
1.當一個抽象模型有兩個方面, 其中一個方面依賴於另外一方面。將這兩者封裝在獨立的對象中以使它們能夠各自獨立地改變和複用。
2.當對一個對象的改變須要同時改變其它對象, 而不知道具體有多少對象有待改變。
3.當一個對象必須通知其它對象,而它又不能假定其它對象是誰。換言之, 你不但願這些對象是緊密耦合的。
生活中的例子:
觀察者定義了對象間一對多的關係,當一個對象的狀態變化時,全部依賴它的對象都獲得通知而且自動地更新。在ATM取款,當取款成功後,以手機、郵件等方式進行通知。
代碼實現:
1 public class BankAccount
2 {
3 Emailer emailer; //強信賴關係
4 Mobile phoneNumber; //強信賴關係
5
6 private double _money;
7
8 public Emailer Emailer
9 {
10 get { return emailer; }
11 set { this.emailer = value; }
12 }
13 public Mobile PhoneNumber
14 {
15 get { return phoneNumber; }
16 set { this.phoneNumber = value; }
17 }
18 public double Money
19 {
20 get { return _money; }
21 set { this._money = value; }
22 }
23
24 public void WithDraw()
25 {
26 emailer.SendEmail(this);
27 phoneNumber.SendNotification(this);
28 }
29
30 }
1 public class Emailer
2 {
3 private string _emailer;
4 public Emailer(string emailer)
5 {
6 this._emailer = emailer;
7 }
8 public void SendEmail(BankAccount ba)
9 {
10 //..
11 Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emailer, ba.Money);
12 }
13 }
1 public class Mobile
2 {
3 private long _phoneNumber;
4 public Mobile(long phoneNumber)
5 {
6 this._phoneNumber = phoneNumber;
7 }
8 public void SendNotification(BankAccount ba)
9 {
10 Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, ba.Money);
11 }
12 }
此時簡單的客戶端調用以下:
1 class Test
2 {
3 static void Main(string[] args)
4 {
5 BankAccount ba = new BankAccount();
6 Emailer emailer = new Emailer("abcdwxc@163.com");
7 Mobile mobile = new Mobile(13901234567);
8 ba.Emailer = emailer;
9 ba.PhoneNumber = mobile;
10 ba.Money = 2000;
11 ba.WithDraw();
12 }
13 }
運行結果以下:
因而可知程序能夠正常運行,但請注意BandAccount和Emailer及Mobile之間造成了一種雙向的依賴關係,即BankAccount調用了Emailer及Mobile的方法,而Emailer及Mobile調用了BnadAccount類的屬性。若是有其中一個類變化,有可能會引發另外一個的變化。若是又需添加一種新的通知方式,就得在BankAccount的WithDraw()方法中增長對該中通知方式的調用。
顯然這樣的設計極大的違背了「開放-封閉」原則,這不是咱們所想要的,僅僅是新增長了一種通知對象,就須要對原有的BankAccount類進行修改,這樣的設計是很糟糕的。對此作進一步的抽象,既然出現了多個通知對象,咱們就爲這些對象之間抽象出一個接口,用它來取消BankAccount和具體的通知對象之間依賴。
由此咱們由左圖轉換到右圖。
實例代碼以下:
1 public interface IObserverAccount
2 {
3 void Update(BankAccount ba);
4 }
1 public class BankAccount
2 {
3 IObserverAccount emailer; //依賴於接口
4 IObserverAccount phoneNumber; //依賴於接口
5
6 private double _money;
7
8 public IObserverAccount Emailer
9 {
10 get { return emailer; }
11 set { this.emailer = value; }
12 }
13 public IObserverAccount PhoneNumber
14 {
15 get { return phoneNumber; }
16 set { this.phoneNumber = value; }
17 }
18 public double Money
19 {
20 get { return _money; }
21 set { this._money = value; }
22 }
23
24 public void WithDraw()
25 {
26 emailer.Update(this);
27 phoneNumber.Update(this);
28 }
29
30 }
1 public class Emailer : IObserverAccount
2 {
3 private string _emailer;
4 public Emailer(string emailer)
5 {
6 this._emailer = emailer;
7 }
8 public void Update(BankAccount ba)
9 {
10 //..
11 Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emailer, ba.Money);
12 }
13 }
1 public class Mobile : IObserverAccount
2 {
3 private long _phoneNumber;
4 public Mobile(long phoneNumber)
5 {
6 this._phoneNumber = phoneNumber;
7 }
8 public void Update(BankAccount ba)
9 {
10 Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, ba.Money);
11 }
12 }
客戶端與上方相同,其運行結果也相同。但BankAccount增長和刪除通知對象時,還需對其進行修改。對此咱們再作以下重構,在BankAccount中維護一個IObserver列表,同時提供相應的維護方法。
1 public class BankAccount
2 {
3 private List<IObserverAccount> Observers = new List<IObserverAccount>();
4
5
6 private double _money;
7
8 public double Money
9 {
10 get { return _money; }
11 set { this._money = value; }
12 }
13
14 public void WithDraw()
15 {
16 foreach (IObserverAccount ob in Observers)
17 {
18 ob.Update(this);
19
20 }
21 }
22 public void AddObserver(IObserverAccount observer)
23 {
24 Observers.Add(observer);
25 }
26 public void RemoverObserver(IObserverAccount observer)
27 {
28 Observers.Remove(observer);
29 }
30
31 }
此時客戶端代碼以下:
1 class Test
2 {
3 static void Main(string[] args)
4 {
5 BankAccount ba = new BankAccount();
6 IObserverAccount emailer = new Emailer("abcdwxc@163.com");
7 IObserverAccount mobile = new Mobile(13901234567);
8
9 ba.Money = 2000;
10 ba.AddObserver(emailer);
11 ba.AddObserver(mobile);
12
13 ba.WithDraw();
14 }
15 }
走到這一步,已經有了Observer模式的影子了,BankAccount類再也不依賴於具體的Emailer或Mobile,而是依賴於抽象的IObserverAccount。存在着的一個問題是Emailer或Mobile仍然依賴於具體的BankAccount,解決這樣的問題很簡單,只須要再對BankAccount類作一次抽象。以下圖:
1 public abstract class Subject
2 {
3 private List<IObserverAccount> Observers = new List<IObserverAccount>();
4
5 private double _money;
6 public Subject(double money)
7 {
8 this._money = money;
9 }
10
11 public double Money
12 {
13 get { return _money; }
14 }
15
16 public void WithDraw()
17 {
18 foreach (IObserverAccount ob in Observers)
19 {
20 ob.Update(this);
21
22 }
23 }
24 public void AddObserver(IObserverAccount observer)
25 {
26 Observers.Add(observer);
27 }
28 public void RemoverObserver(IObserverAccount observer)
29 {
30 Observers.Remove(observer);
31 }
32
33 }
1 public interface IObserverAccount
2 {
3 void Update(Subject subject);
4 }
1 public class BankAccount : Subject
2 {
3 public BankAccount(double money)
4 : base(money)
5 { }
6
7 }
1 public class Emailer : IObserverAccount
2 {
3 private string _emalier;
4 public Emailer(string emailer )
5 {
6 this._emalier = emailer;
7 }
8 public void Update(Subject subject)
9 {
10 Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emalier, subject.Money);
11 }
12 }
1 public class Mobile : IObserverAccount
2 {
3 private long _phoneNumber;
4 public Mobile(long phoneNumber)
5 {
6 this._phoneNumber = phoneNumber;
7 }
8 public void Update(Subject subject)
9 {
10 Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber, subject.Money);
11 }
12 }
此時客戶端實現以下:
1 class Test
2 {
3 static void Main(string[] args)
4 {
5 Subject subject = new BankAccount(2000);
6 subject.AddObserver(new Emailer("abcdwxc@163.com"));
7 subject.AddObserver(new Mobile(13901234567));
8
9 subject.WithDraw();
10 }
11 }
推模式與拉模式
對於發佈-訂閱模型,你們都很容易能想到推模式與拉模式,用SQL Server作過數據庫複製的朋友對這一點很清楚。在Observer模式中一樣區分推模式和拉模式,我先簡單的解釋一下二者的區別:推模式是當有消息時,把消息信息以參數的形式傳遞(推)給全部觀察者,而拉模式是當有消息時,通知消息的方法自己並不帶任何的參數,是由觀察者本身到主體對象那兒取回(拉)消息。知道了這一點,你們可能很容易發現上面我所舉的例子實際上是一種推模式的Observer模式。咱們先看看這種模式帶來了什麼好處:當有消息時,全部的觀察者都會直接獲得所有的消息,並進行相應的處理程序,與主體對象沒什麼關係,二者之間的關係是一種鬆散耦合。可是它也有缺陷,第一是全部的觀察者獲得的消息是同樣的,也許有些信息對某個觀察者來講根本就用不上,也就是觀察者不能「按需所取」;第二,當通知消息的參數有變化時,全部的觀察者對象都要變化。鑑於以上問題,拉模式就應運而生了,它是由觀察者本身主動去取消息,須要什麼信息,就能夠取什麼,不會像推模式那樣獲得全部的消息參數。
拉模式實現以下:
1 public abstract class Subject
2 {
3 private List<IObserverAccount> Observers = new List<IObserverAccount>();
4
5
6 private double _money;
7
8 public double Money
9 {
10 get { return _money; }
11 }
12 public Subject(double money)
13 {
14 this._money = money;
15 }
16 public void WithDraw()
17 {
18 foreach (IObserverAccount ob in Observers)
19 {
20 ob.Update();
21
22 }
23 }
24 public void AddObserver(IObserverAccount observer)
25 {
26 Observers.Add(observer);
27 }
28 public void RemoverObserver(IObserverAccount observer)
29 {
30 Observers.Remove(observer);
31 }
32
33 }
1 public interface IObserverAccount
2 {
3 void Update();
4 }
1 public class BankAccount :Subject
2 {
3 public BankAccount(double money)
4 : base(money)
5 { }
6
7 }
1 public class Emailer : IObserverAccount
2 {
3 private string _emalier;
4 private Subject _subject;
5 public Emailer(string emailer,Subject subject)
6 {
7 this._emalier = emailer;
8 this._subject = subject;
9 }
10 public void Update()
11 {
12 //..
13 Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emalier,_subject.Money);
14 }
15 }
1 public class Mobile : IObserverAccount
2 {
3 private long _phoneNumber;
4 private Subject _subject;
5 public Mobile(long phoneNumber,Subject subject)
6 {
7 this._phoneNumber = phoneNumber;
8 this._subject = subject;
9 }
10 public void Update()
11 {
12 Console.WriteLine("Notified :Phone number is {0} You withdraw {1:C} ", _phoneNumber,_subject.Money);
13 }
14 }
此時客戶端調用以下:
1 class Test
2 {
3 static void Main(string[] args)
4 {
5 Subject subject= new BankAccount(2000);
6 subject.AddObserver(new Emailer("abcdwxc@163.com",subject));
7 subject.AddObserver(new Mobile(13901234567,subject));
8
9 subject.WithDraw();
10 }
11 }
.NET中Observer實現:
用事件和委託來實現Observer模式我認爲更加的簡單和優雅,也是一種更好的解決方案。
1 public class Subject
2 {
3 public event NotifyEventHandler NotifyEvent;
4
5 private double _money;
6 public Subject(double money)
7 {
8 this._money = money;
9 }
10
11 public double Money
12 {
13 get { return _money; }
14 }
15
16 public void WithDraw()
17 {
18 OnNotifyChange();
19 }
20 public void OnNotifyChange()
21 {
22 if (NotifyEvent != null)
23 {
24 NotifyEvent(this);
25 }
26
27 }
28
29 }
1 public class Emailer
2 {
3 private string _emalier;
4 public Emailer(string emailer)
5 {
6 this._emalier = emailer;
7 }
8 public void Update(object obj)
9 {
10 if (obj is Subject)
11 {
12 Subject subject = (Subject)obj;
13
14 Console.WriteLine("Notified : Emailer is {0}, You withdraw {1:C} ", _emalier, subject.Money);
15 }
16 }
17 }
public delegate void NotifyEventHandler(object sender);
客戶端調用以下:
1 class Test
2 {
3 static void Main(string[] args)
4 {
5 Subject subject = new Subject(2000);
6 Emailer emailer = new Emailer("abcdwxc@163.com");
7 subject.NotifyEvent += new NotifyEventHandler(emailer.Update);
8
9
10 subject.WithDraw();
11 }
12 }
Observer實現要點:
1.使用面向對象的抽象,Observer模式使得咱們能夠獨立地改變目標與觀察者,從而使兩者之間的依賴關係達到鬆耦合。
2.目標發送通知時,無需指定觀察者,通知(能夠攜帶通知信息做爲參數)會自動傳播。觀察者本身決定是否須要訂閱通知。目標對象對此一無所知。
3.在C#中的Event。委託充當了抽象的Observer接口,而提供事件的對象充當了目標對象,委託是比抽象Observer接口更爲鬆耦合的設計。
動機(Motivate):
在軟件構建過程當中,若是某一特定領域的問題比較複雜,相似的模式不斷重複出現,若是使用普通的編程方式來實現將面臨很是頻繁的變化。
在這種狀況下,將特定領域的問題表達爲某種文法規則下的句子,而後構建一個解釋器來解釋這樣的句子,從而達到解決問題的目的。
意圖(Intent):
給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
結構圖(Struct):
生活中的例子:
適用性:
1.當有一個語言須要解釋執行,而且你可將該語言中的句子表示爲一個抽象語法樹時,可以使用解釋器模式。
而當存在如下狀況時該模式效果最好:
2.該文法簡單對於複雜的文法,文法的類層次變得龐大而沒法管理。此時語法分析程序生成器這樣的工具是更好的選擇。它們無需構建抽象語法樹便可解釋表達工,這樣能夠節省空間並且還可能節省時間。
3.效率不是一個關鍵問題最高效的解釋器一般不是經過直接解釋語法分析樹實現的,而是首先將它們轉換成另外一種
形式。例如:正則表達式一般被轉換成狀態機。但即便在這種狀況下,轉換器仍可用解釋器模式實現,該模式仍
是有用的。
代碼實現:
客戶端代碼以下:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 string roman = "五千四百三十二"; //5432
6 Context context = new Context(roman);
7
8 //Build the 'parse tree'
9 ArrayList tree = new ArrayList();
10 tree.Add(new OneExpression());
11 tree.Add(new TenExpression());
12 tree.Add(new HundredExpression());
13 tree.Add(new ThousandExpression());
14
15 //Interpret
16 foreach (Expression exp in tree)
17 {
18 exp.Interpret(context);
19 }
20 Console.WriteLine("{0} = {1}", roman, context.Data);
21 //Wait for user
22 Console.Read();
23 }
24 }
建立一個抽象類Expression,來描述共同的操做。
1 public abstract class Expression
2 {
3 protected Dictionary<string, int> table = new Dictionary<string, int>(9);
4 public Expression()
5 {
6 table.Add("一", 1);
7 table.Add("二", 2);
8 table.Add("三", 3);
9 table.Add("四", 4);
10 table.Add("五", 5);
11 table.Add("六", 6);
12 table.Add("七", 7);
13 table.Add("八", 8);
14 table.Add("九", 9);
15 }
16 public virtual void Interpret(Context context)
17 {
18 if(context.Statement.Length==0)
19 {
20 return;
21 }
22 foreach(string key in table.Keys)
23 {
24 int value=table[key];
25 if(context.Statement.EndsWith(key + GetPostifix()))
26 {
27 context.Data +=value*Multiplier();
28 context.Statement = context.Statement.Substring(0,context.Statement.Length- this.GetLength());
29 }
30
31 if(context.Statement.EndsWith("零"))
32 {
33 context.Statement = context.Statement.Substring(0, context.Statement.Length - 1);
34 }
35 if (context.Statement.Length == 0)
36 {
37 return;
38 }
39 }
40 }
41
42 public abstract string GetPostifix();
43 public abstract int Multiplier();
44 public virtual int GetLength()
45 {
46 return this.GetPostifix().Length + 1;
47 }
48 }
而後建立一個公共類Context,定義一些全局信息。
1 public class Context
2 {
3 private string statement;
4 private int data;
5
6 //Constructor
7 public Context(string statement)
8 {
9 this.statement = statement;
10 }
11 //Properties
12 public string Statement
13 {
14 get { return statement; }
15 set { statement = value; }
16 }
17 public int Data
18 {
19 get { return data; }
20 set { data = value; }
21 }
22 }
1 public class OneExpression : Expression
2 {
3 public override string GetPostifix()
4 {
5 return "";
6 }
7 public override int Multiplier() { return 1; }
8 public override int GetLength()
9 {
10 return 1;
11 }
12 }
13 public class TenExpression : Expression
14 {
15 public override string GetPostifix()
16 {
17 return "十";
18 }
19 public override int Multiplier() { return 10; }
20 public override int GetLength()
21 {
22 return 2;
23 }
24 }
25 public class HundredExpression : Expression
26 {
27 public override string GetPostifix()
28 {
29 return "百";
30 }
31 public override int Multiplier() { return 100; }
32 public override int GetLength()
33 {
34 return 2;
35 }
36 }
37 public class ThousandExpression : Expression
38 {
39 public override string GetPostifix()
40 {
41 return "千";
42 }
43 public override int Multiplier() { return 1000; }
44 public override int GetLength()
45 {
46 return 2;
47 }
48 }
Interpreter實現要點:
Interpreter模式的應用場合是interpreter模式應用中的難點,只有知足"業務規則頻繁變化,且相似的模式不斷重複出現,而且容易抽象爲語法規則的問題"才適合使用Interpreter模式。
使用Interpreter模式來表示文法規則,從而能夠使用面向對象技巧來方便地「擴展」文法。
Interpreter模式比較適合簡單的文法表示,對於複雜的文法表示,Interpreter模式會產生比較大的類層次結構,須要求助於語法分析生成器這樣的標準工具。
依賴關係的轉化:
動機(Motivate):
在軟件構建過程當中,常常會出現多個對象互相關聯交互的狀況,對象之間經常會維持一種複雜的引用關係,若是遇到一些需求的更改,這種直接的引用關係將面臨不斷的變化。
在這種狀況下,咱們可以使用一個「中介對象」來管理對象間的關聯關係,避免相互交互的對象之間的緊耦合引用關係,從而更好地抵禦變化。
意圖(Intent):
用一箇中介對象來封裝一系列對象交互。中介者使各對象不須要相互引用,從而使其耦合鬆散,並且能夠獨立地改變它們之間的交互。 ------《設計模式》GOF
結構圖(Struct):
適用性:
1.一組對象以定義良好可是複雜的方式進行通訊。產生的相互依賴關係結構混亂且難以理解。
2.一個對象引用其餘不少對象而且直接與這些對象通訊,致使難以複用該對象。
3.想定製一個分佈在多個類中的行爲,而又不想生成太多的子類。
代碼實現:
1 //Mediator
2 abstract class AbstractChatroom
3 {
4 public abstract void Register(Participant participant);
5 public abstract void Send(string from, string to, string message);
6 }
1 //ConcreteMediator
2 class Chatroom :AbstractChatroom
3 {
4 private Hashtable participants = new Hashtable();
5 public override void Register(Participant participant)
6 {
7 if (participants[participant.Name] == null)
8 {
9 participants[participant.Name]=participant;
10 }
11 participant.Chatroom = this;
12 }
13 public override void Send(string from, string to, string message)
14 {
15 Participant pto = (Participant)participants[to];
16 if (pto != null)
17 {
18 pto.Receive(from, message);
19 }
20 }
21 }
1 //AbstractColleague
2 class Participant
3 {
4 private Chatroom chatroom;
5 private string name;
6
7 //Constructor
8 public Participant(string name)
9 {
10 this.name = name;
11 }
12 //Properties
13 public string Name
14 {
15 get { return name; }
16 }
17 public Chatroom Chatroom
18 {
19 set { chatroom = value; }
20 get { return chatroom; }
21
22 }
23 public void Send(string to, string message)
24 {
25 chatroom.Send(name, to, message);
26 }
27 public virtual void Receive(string from, string message)
28 {
29 Console.WriteLine("{0} to {1}:'{2}'", from, name, message);
30 }
31 }
1 //ConcreteColleaguel
2 class Beatle :Participant
3 {
4 //Constructor
5 public Beatle(string name)
6 : base(name)
7 { }
8 public override void Receive(string from, string message)
9 {
10 Console.Write("To a Beatle: ");
11 base.Receive(from, message);
12 }
13 }
1 //ConcreteColleague2
2 class NonBeatle :Participant
3 {
4 //Constructor
5 public NonBeatle(string name)
6 : base(name)
7 { }
8 public override void Receive(string from, string message)
9 {
10 Console.Write("To a non-Beatle:");
11 base.Receive(from, message);
12 }
13 }
客戶端調用以下:
1 static void Main(string[] args)
2 {
3 //create chatroom
4 Chatroom chatroom = new Chatroom();
5 //Create participants and register them
6 Participant George = new Beatle("George");
7 Participant Paul = new Beatle("Paul");
8 Participant Ringo = new Beatle("Ringo");
9 Participant John = new Beatle("John");
10 Participant Yoko = new Beatle("Yoko");
11 chatroom.Register(George);
12 chatroom.Register(Paul);
13 chatroom.Register(Ringo);
14 chatroom.Register(John);
15 chatroom.Register(Yoko);
16
17 //chatting participants
18 Yoko.Send("John", "Hi John");
19 Paul.Send("Ringo", "All you need is love");
20 Ringo.Send("George", "My sweet Lord");
21 Paul.Send("John", "Can't buy me love");
22 John.Send("Yoko", "My sweet love");
23 }
運行結果以下:
Mediator實現要點:
1.將多個對象間複雜的關聯關係解耦,Mediator模式將多個對象間的控制邏輯進行集中管理,變「多個對象互相關係」爲多「個對象和一箇中介者關聯」,簡化了系統的維護,抵禦了可能的變化。
2.隨着控制邏輯的複雜化,Mediator具體對象的實現可能至關複雜。 這時候能夠對Mediator對象進行分解處理。
3.Facade模式是解耦系統外到系統內(單向)的對象關係關係;Mediator模式是解耦系統內各個對象之間(雙向)的關聯關係。
職責鏈模式(Chain of Responsibility Pattern)
動機(Motivate):
在軟件構建過程當中,一個請求可能被多個對象處理,可是每一個請求在運行時只能有一個接受者,若是顯示指定,將必不可少地帶來請求發送者與接受者的緊耦合。
如何使請求的發送者不須要指定具體的接受者?讓請求的接受者本身在運行時決定來處理請求,從而使二者解耦。
意圖(Intent):
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係。將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。
結構圖(Struct):
適用性:
1.有多個對象能夠處理一個請求,哪一個對象處理該請求運行時刻自動肯定。
2.你想在不明確接收者的狀況下,向多個對象中的一個提交一個請求。
3.可處理一個請求的對象集合應被動態指定。
生活中的例子:
代碼實現:
1 //Handler
2 abstract class Approver
3 {
4 protected Approver successor;
5 public void SetSuccessor(Approver successor)
6 {
7 this.successor = successor;
8 }
9 public abstract void ProcessRequest(Purchase purchase);
10
11 }
12
13
14
1 //ConcreteHandler
2 class Director :Approver
3 {
4 public override void ProcessRequest(Purchase purchase)
5 {
6 if (purchase.Amount < 10000.0)
7 {
8 Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number);
9
10 }
11 else if(successor !=null)
12 {
13 successor.ProcessRequest(purchase);
14 }
15 }
16 }
1
2
3
4 class VicePresident :Approver
5 {
6 public override void ProcessRequest(Purchase purchase)
7 {
8 if (purchase.Amount < 25000.0)
9 {
10 Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number);
11
12 }
13 else if (successor != null)
14 {
15 successor.ProcessRequest(purchase);
16 }
17 }
18 }
1
2 class President :Approver
3 {
4 public override void ProcessRequest(Purchase purchase)
5 {
6 if (purchase.Amount < 100000.0)
7 {
8 Console.WriteLine("{0} approved request# {1}", this.GetType().Name, purchase.Number);
9
10 }
11 else
12 {
13 Console.WriteLine("Request! {0} requires an executive meeting!", purchase.Number);
14 }
15 }
16 }
1
2
3 //Request details
4 class Purchase
5 {
6 private int number;
7 private double amount;
8 private string purpose;
9
10 //Constructor
11 public Purchase(int number, double amount, string purpose)
12 {
13 this.number = number;
14 this.amount = amount;
15 this.purpose = purpose;
16 }
17 //Properties
18 public double Amount
19 {
20 get { return amount; }
21 set { amount = value; }
22 }
23 public string Purpose
24 {
25 get { return purpose; }
26 set { purpose = value; }
27 }
28 public int Number
29 {
30 get { return number; }
31 set { number = value; }
32 }
33 }
客戶端調用以下:
1
2 class Program
3 {
4 static void Main(string[] args)
5 {
6 //Setup Chain of Responsibility
7 Director Larry = new Director();
8 VicePresident Sam = new VicePresident();
9 President Tammy = new President();
10 Larry.SetSuccessor(Sam);
11 Sam.SetSuccessor(Tammy);
12
13 //Generate and process purchase requests
14 Purchase p = new Purchase(1034, 350.00, "Supplies");
15 Larry.ProcessRequest(p);
16
17 p = new Purchase(2035, 32590.10, "Project X");
18 Larry.ProcessRequest(p);
19
20 p = new Purchase(2036, 122100.00, "Project Y");
21 Larry.ProcessRequest(p);
22
23 //Wait for user
24 Console.Read();
25 }
26 }
運行結果以下:
Chain of Responsibility實現要點:
1.Chain of Responsibility模式的應用場合在於「一個請求可能有多個接受者,可是最後真正的接受者只胡一個」,只有這時候請求發送者與接受者的耦合才胡可能出現「變化脆弱」的症狀,職責鏈的目的就是將兩者解耦,從而更好地應對變化。
2.應用了Chain of Responsibility模式後,對象的職責分派將更具靈活性。咱們能夠在運行時動態添加/修改請求的處理職責。
3.若是請求傳遞到職責鏈的未尾仍得不處處理,應該有一個合理的缺省機制。這也是每個接受對象的責任,而不是發出請求的對象的責任。
對象狀態的回溯:
對象狀態的變化無故,如何回溯/恢復對象在某個點的狀態?
動機:
在軟件構建過程當中,某些對象的狀態在轉換過程當中,可能因爲某種須要,要求程序可以回溯到對象以前處於某個點時的狀態。若是使用一些公有接口來讓其餘對象獲得對象的狀態,便會暴露對象的細節實現。
如何實現對象狀態的良好保存與恢復?但同時又不會所以而破壞對象自己的封裝性。
意圖:
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象以外保存這個狀態。這樣之後能夠將該對象恢復到原先保存的狀態。
適用性:
1.必須保存一個對象某一個時刻的(部分)狀態,這樣之後須要時它才能恢復到先前的狀態。
2.若是一個用接口來讓其它對象直接獲得這些狀態,將會暴露對象的實現細節並破壞對象的封裝性。
代碼實現:
1 class Memento
2 {
3 private string name;
4 private string phone;
5 private double budget;
6
7 //Constructor
8 public Memento(string name, string phone, double budget)
9 {
10 this.name = name;
11 this.phone = phone;
12 this.budget = budget;
13 }
14 //Properties
15 public string Name
16 {
17 get { return name; }
18 set { name = value; }
19 }
20 public string Phone
21 {
22 get { return phone; }
23 set { phone = value; }
24 }
25 public double Budget
26 {
27 get { return budget; }
28 set { budget = value; }
29 }
30 }
1 class ProspectMemory
2 {
3 private Memento memento;
4
5 //Property
6 public Memento Memento
7 {
8 set { memento = value; }
9 get { return memento; }
10 }
11 }
1 //Originator
2 class SalesProspect
3 {
4 private string name;
5 private string phone;
6 private double budget;
7
8 //Properties
9 public string Name
10 {
11 get { return name; }
12 set { name = value; Console.WriteLine("Name:" + name); }
13 }
14 public string Phone
15 {
16 get { return phone; }
17 set { phone = value; Console.WriteLine("Phone:" + phone); }
18 }
19 public double Budget
20 {
21 get { return Budget; }
22 set { budget = value; Console.WriteLine("Budget:" + budget); }
23 }
24 public Memento SaveMemento()
25 {
26 Console.WriteLine("\nSaving state --\n");
27 return new Memento(name, phone, budget);
28 }
29 public void RestoreMemento(Memento memento)
30 {
31 Console.WriteLine("\nRestoring state --\n");
32 this.Name = memento.Name;
33 this.Phone = memento.Phone;
34 this.Budget = memento.Budget;
35 }
36 }
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 SalesProspect s = new SalesProspect();
6 s.Name = "xiaoming";
7 s.Phone = "(010)65236523";
8 s.Budget = 28000.0;
9
10 //Store internal state
11 ProspectMemory m = new ProspectMemory();
12 m.Memento = s.SaveMemento();
13
14 //Continue changing originator
15 s.Name = "deke";
16 s.Phone = "(029)85423657";
17 s.Budget = 80000.0;
18
19 //Restore saved state
20 s.RestoreMemento(m.Memento);
21
22 //Wait for user
23 Console.Read();
24 }
25 }
Memento須要注意的幾個要點:
1.備忘錄(Memento)存儲原發器(Originator)對象的內部狀態,在須要時恢復原發器狀態。Memento模式適用於「由原發器管理,卻又必須存儲在原發器以外的信息」。
2.在實現Memento模式中,要防止原發器之外的對象訪問備忘錄對象。備忘錄對象有兩個接口,一個爲原發器的寬接口;一個爲其餘對象使用的窄接口。
3.在實現Memento模式時,要考慮拷貝對象狀態的效率問題,若是對象開銷比較大,能夠採用某種增量式改變來改進Memento模式。
算法與對象的耦合:
對象可能常常須要使用多種不一樣的算法,可是若是變化頻繁,會將類型變得脆弱...
動機:
在軟件構建過程當中,某些對象使用的算法可能多種多樣,常常改變,若是將這些算法都編碼對象中,將會使對象變得異常複雜;並且有時候支持不使用的算法也是一個性能負擔。
如何在運行時根據須要透明地更改對象的算法?將算法與對象自己解耦,從而避免上述問題?
意圖:
定義一系統的算法,把它們一個個封裝起來,而且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
--------《設計模式》GOF
適用性:
1.許多相關的類僅僅是行爲有異。「策略」提供了一種用多個行爲中的一個行爲來配置一個類的方法。
2.須要使用一個算法的不一樣變體。例如,你可能會定義一些反映不一樣的空間/時間權衡的算法。當這些變體實現爲一個算法的類層次時[H087],能夠使用策略模式。
3.算法使用客戶不該該知道數據。可以使用策略模式以免暴露覆雜的,與算法相關的數據結構。
4.一個類定義了多種行爲,而且這些行爲在這個類的操做中以多個條件語句的形式出現。將相關的條件分支移入它們各自的Strategy類中以代替這些條件語句。
代碼實現:
1 enum SortType
2 {
3 QuickSort,
4 ShellSort,
5 MergeSort,
6 }
1 class Sort
2 {
3 public void SortList(SortType s)
4 {
5 if (s == SortType.QuickSort)
6 {
7 ProcessA();
8 }
9 else if (s == SortType.ShellSort)
10 {
11 ProcessB();
12 }
13 else if (s == SortType.MergeSort)
14 {
15 ProcessC();
16 }
17 Console.WriteLine();
18 }
19
20 protected void ProcessA()
21 {
22 Console.WriteLine("QuickSort List");
23 }
24 protected void ProcessB()
25 {
26 Console.WriteLine("ShellSort List");
27 }
28 protected void ProcessC()
29 {
30 Console.WriteLine("MergeSort List");
31 }
32 }
客戶端調用:
1 class Test
2 {
3 public static void Main()
4 {
5 Sort sort = new Sort();
6 sort.SortList(SortType.QuickSort);
7 sort.SortList(SortType.ShellSort);
8 sort.SortList(SortType.MergeSort);
9 }
10 }
因而可知,因爲客戶端新增調用方式的選擇,就會修改SortType及Sort裏的判斷語句。在類Sort中會增長if語句的判斷,用敏捷軟件開發的語言說,你應該聞到了代碼的臭味道了,也就是設計模式中說的存在了變化的地方。
重構以上代碼,增長一層中間層來處理變化。類結構以下:
1 //Stategy 表達抽象算法
2 abstract class SortStrategy
3 {
4 public abstract void Sort(ArrayList list);
5 }
1 //ConcreteStrategy
2 class ShellSort :SortStrategy
3 {
4 public override void Sort(System.Collections.ArrayList list)
5 {
6 list.Sort(); //no-implement
7 Console.WriteLine("ShellSorted List");
8
9 }
10 }
1 //ConcreteStrategy
2 class MergeSort :SortStrategy
3 {
4 public override void Sort(System.Collections.ArrayList list)
5 {
6 list.Sort(); //no-implement
7 Console.WriteLine("MergeSort List ");
8 }
9 }
1 //ConcreteStrategy
2 class QuickSort :SortStrategy
3 {
4 public override void Sort(System.Collections.ArrayList list)
5 {
6 list.Sort(); //Default is Quicksort
7 Console.WriteLine("QuickSorted List");
8 }
9 }
1 //Context
2 class SortdList
3 {
4 private ArrayList list = new ArrayList();
5 private SortStrategy sortstrategy; //對象組合
6 public void SetSortStrategy(SortStrategy sortstrategy)
7 {
8 this.sortstrategy = sortstrategy;
9 }
10 public void Add(string name)
11 {
12 list.Add(name);
13 }
14 public void Sort()
15 {
16 sortstrategy.Sort(list);
17 //Display results
18 foreach (string name in list)
19 {
20 Console.WriteLine(" " + name);
21 }
22 Console.WriteLine();
23 }
24 }
客戶端代碼以下:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 //Two contexts following different strategies
6 SortdList studentRecords = new SortdList();
7
8 studentRecords.Add("Satu");
9 studentRecords.Add("Jim");
10 studentRecords.Add("Palo");
11 studentRecords.Add("Terry");
12 studentRecords.Add("Annaro");
13
14 studentRecords.SetSortStrategy(new QuickSort());
15 studentRecords.Sort();
16
17 studentRecords.SetSortStrategy(new ShellSort());
18 studentRecords.Sort();
19
20 studentRecords.SetSortStrategy(new MergeSort());
21 studentRecords.Sort();
22
23 Console.Read();
24 }
25 }
因而可知,更好地知足開放封閉原則。
Strategy模式的幾個要點:
1.Strategy及其子類爲組件提供了一系列可重用的算法,從而能夠使得類型在運行時方便地根據須要在各個算法之間進行切換。所謂封裝算法,支持算法的變化。
2.Strategy模式提供了用條件判斷語句之外的另外一種選擇,消除條件判斷語句,就是在解耦合。含有許多條件判斷語句的代碼一般都須要Strategy模式。
3.與State相似,若是Strategy對象沒有實例變量,那麼各個上下文能夠共享同一個Strategy對象,從而節省對象開銷。
類層次結構的變化:
類層次結構中可能常常因爲引入新的操做,從而將類型變得脆弱...
動機:
在軟件構建過程當中,因爲需求的改變,某些類層次結構中經常須要增長新的行爲(方法),若是直接在基類中作這樣的更改,將會給子類帶來很繁重的變動負擔,甚至破壞原有設計。
如何在不更改類層次結構的前提下,在運行時根據須要透明地爲類層次結構上的各個類動態添加新的操做,從而避免上述問題?
意圖:
表示一個做用於某對象結構中的各元素的操做。它使你能夠在不改變各元素的類的前提下定義做用於這引發元素的新操做。
結構:
適用性:
1.一個對象結構包含不少類對象,它們有不一樣的接口,而你想對這些對象實施一些依賴於其具體類的操做。
2.須要對一個對象結構中的對象進行不少不一樣的而且不相關的操做,而你想避免讓這些操做"污染"這些對象的類。Visitor使得你能夠將相關的操做集中起來定義在一個類中。當該對象結構被不少應用共享時,用Visitor模式讓每一個應用僅包含須要用到的操做。
3.定義對象結構的類不多改變,但常常須要在結構上定義新的操做。改變對象結構類須要重定義對全部訪問者的接口,這可能須要很大的代價。若是對象結構類常常改變,那麼可能仍是在這些類中定義這些操做較好。
代碼實現:
1 // MainApp startup application
2
3 class MainApp
4 {
5 static void Main()
6 {
7 // Setup employee collection
8 Employees e = new Employees();
9 e.Attach(new Clerk());
10 e.Attach(new Director());
11 e.Attach(new President());
12
13 // Employees are 'visited'
14 e.Accept(new IncomeVisitor());
15 e.Accept(new VacationVisitor());
16
17 // Wait for user
18 Console.Read();
19 }
20 }
21
22 // "Visitor"
23
24 interface IVisitor
25 {
26 void Visit(Element element);
27 }
28
29 // "ConcreteVisitor1"
30
31 class IncomeVisitor : IVisitor
32 {
33 public void Visit(Element element)
34 {
35 Employee employee = element as Employee;
36
37 // Provide 10% pay raise
38 employee.Income *= 1.10;
39 Console.WriteLine("{0} {1}'s new income: {2:C}",
40 employee.GetType().Name, employee.Name,
41 employee.Income);
42 }
43 }
44
45 // "ConcreteVisitor2"
46
47 class VacationVisitor : IVisitor
48 {
49 public void Visit(Element element)
50 {
51 Employee employee = element as Employee;
52
53 // Provide 3 extra vacation days
54 Console.WriteLine("{0} {1}'s new vacation days: {2}",
55 employee.GetType().Name, employee.Name,
56 employee.VacationDays);
57 }
58 }
59
60 class Clerk : Employee
61 {
62 // Constructor
63 public Clerk() : base("Hank", 25000.0, 14)
64 {
65 }
66 }
67
68 class Director : Employee
69 {
70 // Constructor
71 public Director() : base("Elly", 35000.0, 16)
72 {
73 }
74 }
75
76 class President : Employee
77 {
78 // Constructor
79 public President() : base("Dick", 45000.0, 21)
80 {
81 }
82 }
83
84 // "Element"
85
86 abstract class Element
87 {
88 public abstract void Accept(IVisitor visitor);
89 }
90
91 // "ConcreteElement"
92
93 class Employee : Element
94 {
95 string name;
96 double income;
97 int vacationDays;
98
99 // Constructor
100 public Employee(string name, double income,
101 int vacationDays)
102 {
103 this.name = name;
104 this.income = income;
105 this.vacationDays = vacationDays;
106 }
107
108 // Properties
109 public string Name
110 {
111 get{ return name; }
112 set{ name = value; }
113 }
114
115 public double Income
116 {
117 get{ return income; }
118 set{ income = value; }
119 }
120
121 public int VacationDays
122 {
123 get{ return vacationDays; }
124 set{ vacationDays = value; }
125 }
126
127 public override void Accept(IVisitor visitor)
128 {
129 visitor.Visit(this);
130 }
131 }
132
133 // "ObjectStructure"
134
135 class Employees
136 {
137 private ArrayList employees = new ArrayList();
138
139 public void Attach(Employee employee)
140 {
141 employees.Add(employee);
142 }
143
144 public void Detach(Employee employee)
145 {
146 employees.Remove(employee);
147 }
148
149 public void Accept(IVisitor visitor)
150 {
151 foreach (Employee e in employees)
152 {
153 e.Accept(visitor);
154 }
155 Console.WriteLine();
156 }
157 }
運行結果:
Visoitr模式的幾個要點:
1.Visitor模式經過所謂雙重分發(double dispatch)來實如今不更改Element類層次結構的前提下,在運行時透明地爲類層次結構上的各個類動態添加新的操做。
2.所謂雙重分發卻Visotor模式中間包括了兩個多態分發(注意其中的多態機制);第一個爲accept方法的多態辨析;第二個爲visitor方法的多態辨析。
3.Visotor模式的最大缺點在於擴展類層次結構(增添新的Element子類),會致使Visitor類的改變。所以Visiotr模式適用"Element"類層次結構穩定,而其中的操做卻常常面臨頻繁改動".
對象狀態影響對象行爲:
對象擁有不一樣的狀態,每每會行使不一樣的行爲...
動機:
在軟件構建過程當中,某些對象的狀態若是改變以及其行爲也會隨之而發生變化,好比文檔處於只讀狀態,其支持的行爲和讀寫狀態支持的行爲就可能徹底不一樣。
如何在運行時根據對象的狀態來透明更改對象的行爲?而不會爲對象操做和狀態轉化之間引入緊耦合?
意圖:
容許一個對象在其內部狀態改變時改變它的行爲。從而使對象看起來彷佛修改了其行爲。 ------《設計模式》GOF
結構圖:
適用性:
1.一個對象的行爲取決於它的狀態,而且它必須在運行時刻根據狀態改變它的行爲。
2.一個操做中含有龐大的多分支的等條件語句,且這些分支依賴於該對象的狀態。這個狀態一般用一個或多個枚舉常量表示。一般,有多個操做包含這一相同的條件結構。State模式將每個分支放入一個獨立的類中。這使得你可根據對象自身的狀況將對象的狀態做爲一個對象,這一對象能夠不依賴於其餘對象而獨立變化。
代碼實現:
class MainApp
{
static void Main()
{
// Open a new account
Account account = new Account("Jim Johnson");
// Apply financial transactions
account.Deposit(500.0);
account.Deposit(300.0);
account.Deposit(550.0);
account.PayInterest();
account.Withdraw(2000.00);
account.Withdraw(1100.00);
// Wait for user
Console.Read();
}
}
// "State"
abstract class State
{
protected Account account;
protected double balance;
protected double interest;
protected double lowerLimit;
protected double upperLimit;
// Properties
public Account Account
{
get{ return account; }
set{ account = value; }
}
public double Balance
{
get{ return balance; }
set{ balance = value; }
}
public abstract void Deposit(double amount);
public abstract void Withdraw(double amount);
public abstract void PayInterest();
}
// "ConcreteState"
// Account is overdrawn
class RedState : State
{
double serviceFee;
// Constructor
public RedState(State state)
{
this.balance = state.Balance;
this.account = state.Account;
Initialize();
}
private void Initialize()
{
// Should come from a datasource
interest = 0.0;
lowerLimit = -100.0;
upperLimit = 0.0;
serviceFee = 15.00;
}
public override void Deposit(double amount)
{
balance += amount;
StateChangeCheck();
}
public override void Withdraw(double amount)
{
amount = amount - serviceFee;
Console.WriteLine("No funds available for withdrawal!");
}
public override void PayInterest()
{
// No interest is paid
}
private void StateChangeCheck()
{
if (balance > upperLimit)
{
account.State = new SilverState(this);
}
}
}
// "ConcreteState"
// Silver is non-interest bearing state
class SilverState : State
{
// Overloaded constructors
public SilverState(State state) :
this( state.Balance, state.Account)
{
}
public SilverState(double balance, Account account)
{
this.balance = balance;
this.account = account;
Initialize();
}
private void Initialize()
{
// Should come from a datasource
interest = 0.0;
lowerLimit = 0.0;
upperLimit = 1000.0;
}
public override void Deposit(double amount)
{
balance += amount;
StateChangeCheck();
}
public override void Withdraw(double amount)
{
balance -= amount;
StateChangeCheck();
}
public override void PayInterest()
{
balance += interest * balance;
StateChangeCheck();
}
private void StateChangeCheck()
{
if (balance < lowerLimit)
{
account.State = new RedState(this);
}
else if (balance > upperLimit)
{
account.State = new GoldState(this);
}
}
}
// "ConcreteState"
// Interest bearing state
class GoldState : State
{
// Overloaded constructors
public GoldState(State state)
: this(state.Balance,state.Account)
{
}
public GoldState(double balance, Account account)
{
this.balance = balance;
this.account = account;
Initialize();
}
private void Initialize()
{
// Should come from a database
interest = 0.05;
lowerLimit = 1000.0;
upperLimit = 10000000.0;
}
public override void Deposit(double amount)
{
balance += amount;
StateChangeCheck();
}
public override void Withdraw(double amount)
{
balance -= amount;
StateChangeCheck();
}
public override void PayInterest()
{
balance += interest * balance;
StateChangeCheck();
}
private void StateChangeCheck()
{
if (balance < 0.0)
{
account.State = new RedState(this);
}
else if (balance < lowerLimit)
{
account.State = new SilverState(this);
}
}
}
// "Context"
class Account
{
private State state;
private string owner;
// Constructor
public Account(string owner)
{
// New accounts are 'Silver' by default
this.owner = owner;
state = new SilverState(0.0, this);
}
// Properties
public double Balance
{
get{ return state.Balance; }
}
public State State
{
get{ return state; }
set{ state = value; }
}
public void Deposit(double amount)
{
state.Deposit(amount);
Console.WriteLine("Deposited {0:C} --- ", amount);
Console.WriteLine(" Balance = {0:C}", this.Balance);
Console.WriteLine(" Status = {0}\n" ,
this.State.GetType().Name);
Console.WriteLine("");
}
public void Withdraw(double amount)
{
state.Withdraw(amount);
Console.WriteLine("Withdrew {0:C} --- ", amount);
Console.WriteLine(" Balance = {0:C}", this.Balance);
Console.WriteLine(" Status = {0}\n" ,
this.State.GetType().Name);
}
public void PayInterest()
{
state.PayInterest();
Console.WriteLine("Interest Paid --- ");
Console.WriteLine(" Balance = {0:C}", this.Balance);
Console.WriteLine(" Status = {0}\n" ,
this.State.GetType().Name);
}
}
結果:
State模式的幾個要點:
1.State模式將全部一個特定狀態相關的行爲都放入一個State的子類對象中,在對象狀態切換時,切換相應的對象;但同時維持State的接口,這樣實現了具體操做與狀態轉換之間的解耦。
2.爲不一樣的狀態引入不一樣的對象使得狀態轉換變得更加明確,並且能夠保證不會出現狀態不一致的狀況,由於轉換是原子性的----即要麼完全轉換過來,要麼不轉換。
3.若是State對象沒有實例變量,那麼各個上下文能夠共享 同一個State對象,從而節省對象開銷。
阿斯頓法國紅酒看來