設計模式學習第一天:23種設計模式(全)


C#常見的設計模式

一、概要:

 模式分為三種,設計模式、體系結構模式與慣用法。其中慣用法是一種語言緊密相關的模式,例如,定界加鎖模式其實是一種慣用法。

C#項目開發過程中,很多情況下您已經使用了某些模式,但或許您並不知道自己所使用的這種解決方案是一種已經被總結歸納的模式。

工廠、策略、橋接、模板方法、代理等等23Gof經典模式是屬於設計模式,設計模式的粒度相對較小,基本上用於提高模塊內部的可擴展性和可維護性需求

三層、MVCIoC/DI等屬於體系結構模式,粒度比設計模式大,它是從項目的整體角度去看項目的架構。設計需要合理性,架構同樣追求合理性,這就是架構模式的用途。

C#的經典樣例petshop中,采用分層思想構架了整個網站,這里的分層就是體系結構模式;而在數據庫訪問層,則采用工廠模式來泛化數據庫操作,使得業務層不需要關心您現在的數據庫是SQL server的,還是oracle的。這就是設計模式的使用。

模式應用不是一兩句話能夠說清楚的,也不是一天兩天能夠體會的,需要樓主慢慢體會與學習。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

二、分類目錄:

 

創建型:

        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

 

1.1節 工期

How are you

1.1.1 完成日期

1.2節 創建型模式

1.2.1 單件模式(Singleton Pattern)

動機(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的原則。
優點: 簡潔,易懂
缺點: 不可以實現帶參數實例的創建

 

 

1.2.2 抽象工程模式(Abstract Factory)

常規的對象創建方法:

//創建一個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模式共同組合來應對對象創建”的需求變化

 

1.2.3 建造者模式(Builder)

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     }

ChineseBuilderRomanBuilder這兩個是:這個復雜對象的兩個部分經常面臨着劇烈的變化。

 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模式組合使用

 

1.2.4 工廠方法模式(Factory Method)

耦合關系:
       
動機(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模式解決"對象部分"的需求變化

1.2.5 原型模式(Prototype)

依賴關系倒置:    

         

動機(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)

適配器模式(Adapter Pattern)

適配(轉換)的概念無處不在......
適配,即在不改變原有實現的基礎上,將原先不兼容的接口轉換為兼容的接口。
例如:二轉換為三箱插頭,將高電壓轉換為低電壓等。
    
動機(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 //對象適配器(AdapterAdaptee組合的關系
 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方法中對兩個對象進行比較。

橋接模式(Bridge Pattern) 

動機(Motivate):
    在軟件系統中,某些類型由於自身的邏輯,它具有兩個或多個維度的變化,那么如何應對這種“多維度的變化”?如何利用面向對象的技術來使得該類型能夠輕松的沿着多個方向進行變化,而又不引入額外的復雜度?

意圖(Intent):
    將抽象部分與實現部分分離,使它們都可以獨立的變化。
                                  ------《設計模式》GOF
結構圖(Struct):
                   

生活中的例子:      

    我想大家小時候都有用蠟筆畫畫的經歷吧。紅紅綠綠的蠟筆一大盒,根據想象描繪出格式圖樣。而毛筆下的國畫更是工筆寫意,各展風采。而今天我們的故事從蠟筆與毛筆說起。

    設想要繪制一幅圖畫,藍天、白雲、綠樹、小鳥,如果畫面尺寸很大,那么用蠟筆繪制就會遇到點麻煩。畢竟細細的蠟筆要塗出一片藍天,是有些麻煩。如果有可能,最好有套大號蠟筆,粗粗的蠟筆很快能塗抹完成。至於色彩嗎,最好每種顏色來支粗的,除了藍天還有綠地呢。這樣,如果一套12種顏色的蠟筆,我們需要兩套 24支,同種顏色的一粗一細。呵呵,畫還沒畫,開始做夢了:要是再有一套中號蠟筆就更好了,這樣,不多不少總共36支蠟筆。

 

           

   再看看毛筆這一邊,居然如此簡陋:一套水彩12色,外加大中小三支毛筆。你可別小瞧這"簡陋"的組合,畫藍天用大毛筆,畫小鳥用小毛筆,各具特色。

 

          

       呵呵,您是不是已經看出來了,不錯,我今天要說的就是Bridge模式。為了一幅畫,我們需要准備36支型號不同的蠟筆,而改用毛筆三支就夠了,當然還要搭配上12種顏料。通過Bridge模式,我們把乘法運算3×1236改為了加法運算31215,這一改進可不小。那么我們這里蠟筆和毛筆到底有什么區別呢?

    實際上,蠟筆和毛筆的關鍵一個區別就在於筆和顏色是否能夠分離。【GOF95】橋梁模式的用意是"將抽象化 (Abstraction)與實現化(Implementation)脫耦,使得二者可以獨立地變化"。關鍵就在於能否脫耦。蠟筆的顏色和蠟筆本身是分不開的,所以就造成必須使用36支色彩、大小各異的蠟筆來繪制圖畫。而毛筆與顏料能夠很好的脫耦,各自獨立變化,便簡化了操作。在這里,抽象層面的概念是: "毛筆用顏料作畫",而在實現時,毛筆有大中小三號,顏料有紅綠藍等12種,於是便可出現3×12種組合。每個參與者(毛筆與顏料)都可以在自己的自由度上隨意轉換。

    蠟筆由於無法將筆與顏色分離,造成筆與顏色兩個自由度無法單獨變化,使得只有創建36種對象才能完成任務。Bridge模式將繼承關系轉換為組合關系,從而降低了系統間的耦合,減少了代碼編寫量。  

代碼實現: 
    

1   abstract class Brush
2     {
3         protected Color c;
4         public abstract void Paint();

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模式。 

裝飾模式(Decorator Pattern) 

子類復子類,子類何其多

    假如我們需要為游戲中開發一種坦克,除了各種不同型號的坦克外,我們還希望在不同場合中為其增加以下一種或多種功能;比如紅外線夜視功能,比如水陸兩棲功能,比如衛星定位功能等等。
按類繼承的作法如下:
   

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 }

組合模式(Composite Pattern) 

動機(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模式在具體實現中,可以讓父對象中的子對象反向追溯;如果父對象有頻繁的遍歷需求,可使用緩存技巧來改善效率。

外觀模式(Facade Pattern) 

動機(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);

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 

LINQ是什么?
它是Language Integrated Query
當我們要對數據庫表進行查詢的時候,我們一定會編寫 "select * from sometable where ID = .."的語句。好,那我們現在根據LINQ的語法,完全可以將我們熟悉的SQL中像"select","from","where"等語句在.NET Framework環境中順利使用並且大大提高開發的效率。

下面我就牛刀小試,做個demo看看。

1. 先下載LinQ框架 
    現在最新版本是20065月發布"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 ...

享元模式(Flyweight Pattern) 

面向對象的代價
    面向對象很好地解決了系統抽象性的問題,同時在大多數情況下,也不會損及系統的性能。但是,在
某些特殊的應用中下,由於對象的數量太大,采用面向對象會給系統帶來難以承受的內存開銷。比如:
圖形應用中的圖元等對象、字處理應用中的字符對象等。 
    
                         
 動機(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.享元模式的優點在於它大幅度地降低內存中對象的數量。但是,它做到這一點所付出的代價也是很高的:享元模式使得系統更加復雜。為了使對象可以共享,需要將一些狀態外部化,這使得程序的邏輯復雜化。另外它將享元對象的狀態外部化,而讀取外部狀態使得運行時間稍微變長。

代理模式(Proxy Pattern) 

直接與間接:
   人們對復雜的軟件系統常有一種處理手法,即增加一層間接層,從而對系統獲得一種更為靈活、
滿足特定需求的解決方案。     
                                                                                                             
     
動機(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基礎知識匯總 

[.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頁面是結構化的。每個頁面都是一個繼承了.NETSystem.Web.UI.Page的類。
另外ASP.NET的后台編碼功能允許進一步采用結構化的方式.
頁面請求是和WEB服務器在編譯后高速緩存ASP.NET頁面。

[.NET(C#)]
覆蓋(override)和重載(overload:
覆蓋是指子類重新定義父類的虛函數的做法。
重載,是指允許存在多個同名函數,而這些函數的參數表不同(或許參數個數不同,或許參數類型不同,或許兩者都不同)。
其實,重載的概念並不屬於面向對象編程
重載的實現是:編譯器根據函數不同的參數表,對同名函數的名稱做修飾
然后這些同名函數就成了不同的函數(至少對於編譯器來說是這樣的)。
如,有兩個同名函數:function    func(p:integer):integer;    function  func(p:string):integer;
那么編譯器做過修飾后的函數名稱可能是這樣的:int_funcstr_func
對於這兩個函數的調用,在編譯器間就已經確定了,是靜態的(記住:是靜態)。
也就是說,它們的地址在編譯期就綁定了(早綁定),
因此,重載和多態無關!真正和多態相關的是覆蓋
當子類重新定義了父類的虛函數后,父類指針根據賦給它的不同的子類指針,動態(記住:是動態!)的調用屬於子類的該函數,
這樣的函數調用在編譯期間是無法確定的(調用的子類的虛函數的地址無法給出)。
因此,這樣的函數地址是在運行期綁定的(晚邦定)。
結論就是:重載只是一種語言特性,與多態無關,與面向對象也無關! 

[.NET(C#)]
C#refout的區別:
方法參數上的 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#)]
ADOADO.NET的區別:
ADO使用OLE DB接口並基於微軟的COM技術
ADO.NET擁有自己的ADO.NET接口並且基於微軟的.NET體系架構。
ADORecordset存儲,而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構架提供了優化的數據訪問模型,和基於COMADO是完全兩樣的數據訪問方式。

ado.netado存在着比較大的差異:
1ado.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 drivermicrosoft odbc driver for oraclemicrosoft jet odbc driver
3ado.net提供了兩個隊形來處理從數據源中抽取數據,它們是datasetdatareader對象。
前者是記錄在內存中的緩存,您可以從任何方向隨意訪問和修改。
后者是高度優化的對象,專為以僅向前方式滾動只讀記錄而設計。
4ado.net統一了數據容器類編程接口,無論您打算編寫何種應用程序,windows窗體,web窗體還是web服務
都可以通過同一組類來處理數據。
不管在后端的數據源數sql server數據庫,ole dbxml文件還是一個數組
您都可以通過相同的方法和屬性來滾動和處理它們的內容。
5.在ado中,xml只不過是輸入和輸出格式。
然而在ado.net中,xml是一種數據格式,提供了操作,組織,共享和傳遞數據的手段。

ADONET相對於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#)]
DataGridDatasouse可以連接什么數據源
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#)]
詳述.NETclassstruct的異同
結構與類共享幾乎所有相同的語法,但結構比類受到的限制更多:
盡管結構的靜態字段可以初始化,結構實例字段聲明還是不能使用初始值設定項。
結構不能聲明默認構造函數(沒有參數的構造函數)或析構函數。
結構的副本由編譯器自動創建和銷毀,因此不需要使用默認構造函數和析構函數。
實際上,編譯器通過為所有字段賦予默認值(參見默認值表)來實現默認構造函數。
結構不能從類或其他結構繼承。
結構是值類型 -- 如果從結構創建一個對象並將該對象賦給某個變量,變量則包含結構的全部值。
復制包含結構的變量時,將復制所有數據,對新副本所做的任何修改都不會改變舊副本的數據。
由於結構不使用引用,因此結構沒有標識 -- 具有相同數據的兩個值類型實例是無法區分的。
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 classinterface有什么區別?
答:聲明方法的存在而不去實現它的類被叫做抽像類(abstract class
它用於要創建一個體現某些基本行為的類,並為該類聲明方法,但不能在該類中實現該類的情況。
不能創建abstract 類的實例。
然而可以創建一個變量,其類型是一個抽像類,並讓它指向具體子類的一個實例。
不能有抽像構造函數或抽像靜態方法。
Abstract 類的子類為它們父類中的所有抽像方法提供實現,否則它們也是抽像類。
取而代之,在子類中實現該方法。
知道其行為的其它類可以在類中實現這些方法。

接口(interface)是抽像類的變體。
在接口中,所有方法都是抽像的。
多繼承性可通過實現這樣的接口而獲得。
接口中的所有方法都是抽像的,沒有一個有程序體。
接口只可以定義static final成員變量。
接口的實現與子類相似,除了該實現類不能從接口定義中繼承行為。

當類實現特殊接口時,它定義(即將程序體給予)所有這種接口的方法。
然后,它可以在實現了該接口的類的任何對像上調用接口的方法。
由於有抽像類,它允許使用接口名作為引用變量的類型。通常的動態聯編將生效。
引用可以轉換到接口類型或從接口類型轉換,instanceof 運算符可以用來決定某對象的類是否實現了接口。

接口可以繼承接口。
抽像類可以實現(implements)接口
抽像類是否可繼承實體類(concrete class),但前提是實體類必須有明確的構造函數。

[.NET(C#)]
什么叫應用程序域?什么是托管代碼?什么是強類型系統?
什么是裝箱和拆箱?什么是重載?CTSCLSCLR分別作何解釋?

應用程序域:
應用程序域為安全性、可靠性、版本控制以及卸載程序集提供了隔離邊界。
應用程序域通常由運行庫宿主創建,運行庫宿主負責在運行應用程序之前引導公共語言運行庫。
應用程序域提供了一個更安全、用途更廣的處理單元,公共語言運行庫可使用該單元提供應用程序之間的隔離。
應用程序域可以理解為一種輕量級進程。起到安全的作用。占用資源小。

托管代碼:
使用基於公共語言運行庫的語言編譯器開發的代碼稱為托管代碼;托管代碼具有許多優點,
例如:跨語言集成、跨語言異常處理、增強的安全性、版本控制和部署支持、簡化的組件交互模型、調試和分析服務等。

裝箱和拆箱:
從值類型接口轉換到引用類型:裝箱。
從引用類型轉換到值類型:拆箱。
裝箱和拆箱使值類型能夠被視為對象。
對值類型裝箱將把該值類型打包到 Object 引用類型的一個實例中。
這使得值類型可以存儲於垃圾回收堆中。
拆箱將從對象中提取值類型。

重載:
每個類型成員都有一個唯一的簽名。
方法簽名由方法名稱和一個參數列表(方法的參數的順序和類型)組成。
只要簽名不同,就可以在一種類型內定義具有相同名稱的多種方法。
當定義兩種或多種具有相同名稱的方法時,就稱作重載。

CTS通用類型系統 (common type system) :
一種確定公共語言運行庫如何定義、使用和管理類型的規范。

CLR公共語言運行庫:
.NET Framework 提供了一個稱為公共語言運行庫的運行時環境.
它運行代碼並提供使開發過程更輕松的服務。

CLS公共語言規范:
要和其他對象完全交互,而不管這些對象是以何種語言實現的.
對象必須只向調用方公開那些它們必須與之互用的所有語言的通用功能。
為此定義了公共語言規范 (CLS),它是許多應用程序所需的一套基本語言功能。

強類型:
C# 是強類型語言;因此每個變量和對象都必須具有聲明類型。

[.NET(C#)]
值類型和引用類型的區別? 
基於值類型的變量直接包含值。
將一個值類型變量賦給另一個值類型變量時,將復制包含的值。
這與引用類型變量的賦值不同,引用類型變量的賦值只復制對對象的引用,而不復制對象本身。

所有的值類型均隱式派生自 System.ValueType
與引用類型不同,從值類型不可能派生出新的類型。但與引用類型相同的是,結構也可以實現接口。
與引用類型不同,值類型不可能包含 null 值。然而,可空類型功能允許將 null 賦給值類型。 
每種值類型均有一個隱式的默認構造函數來初始化該類型的默認值。
值類型主要由兩類組成:結構、枚舉
結構分為以下幾類:
Numeric(數值)類型、整型、浮點型、decimalbool、用戶定義的結構。
引用類型的變量又稱為對象,可存儲對實際數據的引用。
聲明引用類型的關鍵字:classinterfacedelegate、內置引用類型: objectstring

                     值類型                    引用類型
 
內存分配地點      分配在棧中                   分配在堆中
效率           效率高,不需要地址轉換          效率低,需要進行地址轉換
內存回收        使用完后,立即回收              使用完后,不是立即回收,等待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#)]
解釋一下UDDIWSDL的意義及其作用
UDDI:統一描述、發現和集成協議(UDDI, Universal Description, Discovery and Integration)
是一套基於Web的、分布式的、為Web服務提供的信息注冊中心的實現標准規范,
同時也包含一組使企業能將自身提供的Web服務注冊以使得別的企業能夠發現的訪問協議的實現標准。
UDDI 提供了一組基於標准的規范用於描述和發現服務,還提供了一組基於因特網的實現。

WSDL:WSDL描述Web服務的公共接口。
這是一個基於XML的關於如何與Web服務通訊和使用的服務描述;
服務 URL 和命名空間 
網絡服務的類型
(可能還包括 SOAP 的函數調用,正像我所說過的,WSDL 足夠自如地去描述網絡服務的廣泛內容) 
有效函數列表 
每個函數的參數 
每個參數的類型 
每個函數的返回值及其數據類型

[.NET(C#)]
什么是SOAP,有哪些應用。
:SOAPSimple Object Access Protocol )簡單對象訪問協議
是在分散或分布式的環境中交換信息並執行遠程過程調用的協議,是一個基於XML的協議。
使用SOAP,不用考慮任何特定的傳輸協議(最常用的還是HTTP協議)
可以允許任何類型的對象或代碼,在任何平台上,以任何一直語言相互通信。
這種相互通信采用的是XML格式的消息。 

SOAP也被稱作XMLP,為兩個程序交換信息提供了一種標准的工作機制。
在各類機構之間通過電子方式相互協作的情況下完全有必要為此制定相應的標准。 

SOAP描述了把消息捆綁為XML的工作方式。
它還說明了發送消息的發送方、消息的內容和地址以及發送消息的時間。
SOAPWeb Service的基本通信協議。
SOAP規范還定義了怎樣用XML來描述程序數據(Program Data,怎樣執行RPCRemote Procedure Call)。
大多數SOAP解決方案都支持RPC-style應用程序。
SOAP還支持 Document-style應用程序(SOAP消息只包含XML文本信息)。 

最后SOAP規范還定義了HTTP消息是怎樣傳輸SOAP消息的。
MSMQSMTPTCP/IP都可以做SOAP的傳輸協議。  

SOAP 是一種輕量級協議,用於在分散型、分布式環境中交換結構化信息。
 SOAP 利用 XML 技術定義一種可擴展的消息處理框架,它提供了一種可通過多種底層協議進行交換的消息結構。 
這種框架的設計思想是要獨立於任何一種特定的編程模型和其他特定實現的語義。 

SOAP 定義了一種方法以便將 XML 消息從 點傳送到 點。 
為此,它提供了一種基於 XML 且具有以下特性的消息處理框架: 
1) 可擴展
2) 可通過多種底層網絡協議使用
3) 獨立於編程模型。 

[.NET(C#)]
如何部署一個ASP.net頁面
VS 2005VS 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#)]
CTSCLSCLR分別作何解釋?
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 構架下remotingwebservice兩項技術的理解以及實際中的應用。
.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#)]
abstractmethod不可同時是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#)]
堆和棧的區別:
棧:由編譯器自動分配、釋放。在函數體中定義的變量通常在棧上。
堆:一般由程序員分配釋放。用newmalloc等分配內存函數分配得到的就是在堆上。

[.NET(C#)]
成員變量和成員函數前加static的作用:
它們被稱為常成員變量和常成員函數,又稱為類成員變量和類成員函數。
分別用來反映類的狀態。
比如類成員變量可以用來統計類實例的數量,類成員函數負責這種統計的動作。

[.NET(C#)]
c#usingnew這兩個關鍵字有什么意義:
using  引入名稱空間或者使用非托管資源
new  新建實例或者隱藏父類方法

[.NET(C#)]
XML即可擴展標記語言。
eXtensible Markup Language.標記是指計算機所能理解的信息符號
通過此種標記,計算機之間可以處理包含各種信息的文章等。
如何定義這些標記,即可以選擇國際通用的標記語言
比如HTML,也可以使用象XML這樣由相關人士自由決定的標記語言,這就是語言的可擴展性。
XML是從SGML中簡化修改出來的。它主要用到的有XMLXSLXPath等。

[.NET(C#)]
什么是code-Behind技術。
答:ASPX,RESXCS三個后綴的文件,這個就是代碼分離.
實現了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#)]
errorexception有什么區別:
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#)]
constreadonly有什么區別? 
const  可以用於局部常量
readonly 實際是類的initonly字段,顯然不能是局部的。

模板方法(Template Method) 

無處不在的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方法。

C#out 及 ref 區別 

方法參數上的 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  {//離開這個函數前,必須對xy賦值,否則會報錯。
 8   //y = x;
 9   //上面這行會報錯,因為使用了out后,xy都清空了,需要重新賦值,即使調用函數前賦過值也不行
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

3 {

5    new public void Invoke() {}

7 }

但是,因為字段 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技術、人工智能等領域。       談到面向對象,這方面的文章非常多。但是,明確地給出對象的定義或說明對象的定義的非常少——至少我現在還沒有發現。其初,面向對象是專指在程序設計中采用封裝、繼承、抽象等設計方法。可是,這個定義顯然不能再適合現在情況。面向對象的思想已經涉及到軟件開發的各個方面。如,面向對象的分析(OOAObject Oriented Analysis),面向對象的設計(OODObject Oriented Design)、以及我們經常說的面向對象的編程實現(OOPObject Oriented Programming)。許多有關面向對象的文章都只是講述在面向對象的開發中所需要注意的問題或所采用的比較好的設計方法。看這些文章只有真正懂得什么是對象,什么是面向對象,才能最大程度地對自己有所裨益。這一點,恐怕對初學者甚至是從事相關工作多年的人員也會對它們的概念模糊不清。 

      面向對象是當前計算機界關心的重點,它是90年代軟件開發方法的主流。面向對象的概念和應用已超越了程序設計和軟件開發,擴展到很寬的范圍。如數據庫系統、交互式界面、應用結構、應用平台、分布式系統、網絡管理結構、CAD技術、人工智能等領域。

一、傳統開發方法存在問題

      1.軟件重用性差
      重用性是指同一事物不經修改或稍加修改就可多次重復使用的性質。軟件重用性是軟件工程追求的目標之一。

      2.軟件可維護性差
      軟件工程強調軟件的可維護性,強調文檔資料的重要性,規定最終的軟件產品應該由完整、一致的配置成分組成。在軟件開發過程中,始終強調軟件的可讀性、可修改性和可測試性是軟件的重要的質量指標。實踐證明,用傳統方法開發出來的軟件,維護時其費用和成本仍然很高,其原因是可修改性差,維護困難,導致可維護性差。

      3.開發出的軟件不能滿足用戶需要
      用傳統的結構化開發大型軟件系統涉及各種不同領域的知識,在開發需求模糊或需求動態變化的系統時,所開發出的軟件系統往往不能真正滿足用戶的需要。

      用結構化方法開發的軟件,其穩定性、可修改性和可重用性都比較差,這是因為結構化方法的本質是功能分解,從代表目標系統整體功能的單個處理着手,自頂向下不斷把復雜的處理分解為子處理,這樣一層一層的分解下去,直到僅剩下若干個容易實現的子處理功能為止,然后用相應的工具來描述各個最低層的處理。因此,結構化方法是圍繞實現處理功能的過程來構造系統的。然而,用戶需求的變化大部分是針對功能的,因此,這種變化對於基於過程的設計來說是災難性的。用這種方法設計出來的系統結構常常是不穩定的 ,用戶需求的變化往往造成系統結構的較大變化,從而需要花費很大代價才能實現這種變化。

二、面向對象的基本概念

      (1)對象。
      對象是人們要進行研究的任何事物,從最簡單的整數到復雜的飛機等均可看作對象,它不僅能表示具體的事物,還能表示抽象的規則、計划或事件。

      (2)對象的狀態和行為。
      對象具有狀態,一個對象用數據值來描述它的狀態。
      對象還有操作,用於改變對象的狀態,對象及其操作就是對象的行為。
      對象實現了數據和操作的結合,使數據和操作封裝於對象的統一體中

      (3)
      具有相同或相似性質的對象的抽象就是類。因此,對象的抽象是類,類的具體化就是對象,也可以說類的實例是對象。
      類具有屬性,它是對象的狀態的抽象,用數據結構來描述類的屬性。
      類具有操作,它是對象的行為的抽象,用操作名和實現該操作的方法來描述。

      (4)類的結構。
      在客觀世界中有若干類,這些類之間有一定的結構關系。通常有兩種主要的結構關系,即一般--具體結構關系,整體--部分結構關系。

      ①一般——具體結構稱為分類結構,也可以說是關系,或者是“is a”關系。
      ②整體——部分結構稱為組裝結構,它們之間的關系是一種關系,或者是“has a”關系。

      (5)消息和方法。
      對象之間進行通信的結構叫做消息。在對象的操作中,當一個消息發送給某個對象時,消息包含接收對象去執行某種操作的信息。發送一條消息至少要包括說明接受消息的對象名、發送給該對象的消息名(即對象名、方法名)。一般還要對參數加以說明,參數可以是認識該消息的對象所知道的變量名,或者是所有對象都知道的全局變量名。

      類中操作的實現過程叫做方法,一個方法有方法名、參數、方法體。消息傳遞如圖10-1所示。

             

二、面向對象的特征

      (1)對象唯一性。
      每個對象都有自身唯一的標識,通過這種標識,可找到相應的對象。在對象的整個生命期中,它的標識都不改變,不同的對象不能有相同的標識。

      (2)分類性。
      分類性是指將具有一致的數據結構(屬性)和行為(操作)的對象抽象成類。一個類就是這樣一種抽象,它反映了與應用有關的重要性質,而忽略其他一些無關內容。任何類的划分都是主觀的,但必須與具體的應用有關。

      (3)繼承性。
      繼承性是子類自動共享父類數據結構和方法的機制,這是類之間的一種關系。在定義和實現一個類的時候,可以在一個已經存在的類的基礎之上來進行,把這個已經存在的類所定義的內容作為自己的內容,並加入若干新的內容。

      繼承性是面向對象程序設計語言不同於其它語言的最重要的特點,是其他語言所沒有的。

      在類層次中,子類只繼承一個父類的數據結構和方法,則稱為單重繼承。
      在類層次中,子類繼承了多個父類的數據結構和方法,則稱為多重繼承。
      在軟件開發中,類的繼承性使所建立的軟件具有開放性、可擴充性,這是信息組織與分類的行之有效的方法,它簡化了對象、類的創建工作量,增加了代碼的可重性。
      采用繼承性,提供了類的規范的等級結構。通過類的繼承關系,使公共的特性能夠共享,提高了軟件的重用性。

      (4)多態性(多形性)
      多態性使指相同的操作或函數、過程可作用於多種類型的對象上並獲得不同的結果。不同的對象,收到同一消息可以產生不同的結果,這種現象稱為多態性。
      多態性允許每個對象以適合自身的方式去響應共同的消息。
      多態性增強了軟件的靈活性和重用性。

三、面向對象的要素

      (1)抽象。 
      抽象是指強調實體的本質、內在的屬性。在系統開發中,抽象指的是在決定如何實現對象之前的對象的意義和行為。使用抽象可以盡可能避免過早考慮一些細節。

      類實現了對象的數據(即狀態)和行為的抽象。
 
      (2)封裝性(信息隱藏)。
      封裝性是保證軟件部件具有優良的模塊性的基礎。
      面向對象的類是封裝良好的模塊,類定義將其說明(用戶可見的外部接口)與實現(用戶不可見的內部實現)顯式地分開,其內部實現按其具體定義的作用域提供保護。
      對象是封裝的最基本單位。封裝防止了程序相互依賴性而帶來的變動影響。面向對象的封裝比傳統語言的封裝更為清晰、更為有力。

      (3)共享性
      面向對象技術在不同級別上促進了共享
      同一類中的共享。同一類中的對象有着相同數據結構。這些對象之間是結構、行為特征的共享關系。
      在同一應用中共享。在同一應用的類層次結構中,存在繼承關系的各相似子類中,存在數據結構和行為的繼承,使各相似子類共享共同的結構和行為。使用繼承來實現代碼的共享,這也是面向對象的主要優點之一。
      在不同應用中共享。面向對象不僅允許在同一應用中共享信息,而且為未來目標的可重用設計准備了條件。通過類庫這種機制和結構來實現不同應用中的信息共享。

      4.強調對象結構而不是程序結構

四、面向對象的開發方法

      目前,面向對象開發方法的研究已日趨成熟,國際上已有不少面向對象產品出現。面向對象開發方法有Coad方法、Booch方法和OMT方法等。

      1.Booch方法

      Booch最先描述了面向對象的軟件開發方法的基礎問題,指出面向對象開發是一種根本不同於傳統的功能分解的設計方法。面向對象的軟件分解更接近人對客觀事務的理解,而功能分解只通過問題空間的轉換來獲得。

      2.Coad方法

      Coad方法是1989CoadYourdon提出的面向對象開發方法。該方法的主要優點是通過多年來大系統開發的經驗與面向對象概念的有機結合,在對象、結構、屬性和操作的認定方面,提出了一套系統的原則。該方法完成了從需求角度進一步進行類和類層次結構的認定。盡管Coad方法沒有引入類和類層次結構的術語,但事實上已經在分類結構、屬性、操作、消息關聯等概念中體現了類和類層次結構的特征。

      3.OMT方法

      OMT方法是1991年由James Rumbaugh5人提出來的,其經典著作為面向對象的建模與設計

      該方法是一種新興的面向對象的開發方法,開發工作的基礎是對真實世界的對象建模,然后圍繞這些對象使用分析模型來進行獨立於語言的設計,面向對象的建模和設計促進了對需求的理解,有利於開發得更清晰、更容易維護的軟件系統。該方法為大多數應用領域的軟件開發提供了一種實際的、高效的保證,努力尋求一種問題求解的實際方法。

      4.UML(Unified Modeling Language)語言

      軟件工程領域在1995年~1997年取得了前所未有的進展,其成果超過軟件工程領域過去15年的成就總和,其中最重要的成果之一就是統一建模語言(UML)的出現。UML將是面向對象技術領域內占主導地位的標准建模語言。UML不僅統一了Booch方法、OMT方法、OOSE方法的表示方法,而且對其作了進一步的發展,最終統一為大眾接受的標准建模語言。UML是一種定義良好、易於表達、功能強大且普遍適用的建模語言。它融入了軟件工程領域的新思想、新方法和新技術。它的作用域不限於支持面向對象的分析與設計,還支持從需求分析開始的軟件開發全過程。

五、面向對象的模型

 ·對象模型

      對象模型表示了靜態的、結構化的系統數據性質,描述了系統的靜態結構,它是從客觀世界實體的對象關系角度來描述,表現了對象的相互關系。該模型主要關心系統中對象的結構、屬性和操作,它是分析階段三個模型的核心,是其他兩個模型的框架

      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.數據存儲對象
      數據流圖中的數據存儲是被動對象,它用來存儲數據。它與動作對象不一樣,數據存儲本身不產生任何操作,它只響應存儲和訪問的要求。 

六、面向對象的分析

      面向對象分析的目的是對客觀世界的系統進行建模。本節以上面介紹的模型概念為基礎,結合銀行網絡系統的具體實例來構造客觀世界問題的准確、嚴密的分析模型。
      分析模型有三種用途:用來明確問題需求;為用戶和開發人員提供明確需求;為用戶和開發人員提供一個協商的基礎,作為后繼的設計和實現的框架。

(一) 面向對象的分析

             

     系統分析的第一步是:陳述需求。分析者必須同用戶一塊工作來提煉需求,因為這樣才表示了用戶的真實意圖,其中涉及對需求的分析及查找丟失的信息。下面以銀行網絡系統為例,用面向對象方法進行開發。
銀行網絡系統問題陳述:設計支持銀行網絡的軟件,銀行網絡包括人工出納站和分行共享的自動出納機。每個分理處用分理處計算機來保存各自的帳戶,處理各自的事務;各自分理處的出納站與分理處計算機通信,出納站錄入帳戶和事務數據;自動出納機與分行計算機通信,分行計算機與撥款分理處結帳,自動出納機與用戶接口接受現金卡,與分行計算機通信完成事務,發放現金,打印收據;系統需要記錄保管和安全措施;系統必須正確處理同一帳戶的並發訪問;每個分處理為自己的計算機准備軟件,銀行網絡費用根據顧客和現金卡的數目分攤給各分理處。
1018給出銀行網絡系統的示意圖。

                        

(二)建立對象模型

      首先標識和關聯,因為它們影響了整體結構和解決問題的方法,其次是增加屬性,進一步描述類和關聯的基本網絡,使用繼承合並和組織類,最后操作增加到類中去作為構造動態模型和功能模型的副產品。

      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.建立數據流圖
   數據流圖說明輸出值是怎樣從輸入值得來的,數據流圖通常按層次組織。

           

            

 

(五)確定操作

   在建立對象模型時,確定了類、關聯、結構和屬性,還沒有確定操作。只有建立了動態模型和功能模型之后,才可能最后確定類的操作。 

七、面向對象的設計

   面向對象設計是把分析階段得到的需求轉變成符合成本和質量要求的、抽象的系統實現方案的過程。從面向對象分析到面向對象設計,是一個逐漸擴充模型的過程。

   瀑布模型把設計進一步划分成概要設計和詳細設計兩個階段,類似地,也可以把面向對象設計再細分為系統設計和對象設計。系統設計確定實現系統的策略和目標系統的高層結構。對象設計確定解空間中的類、關聯、接口形式及實現操作的算法。

(一)面向對象設計的准則

   1.模塊化
   面向對象開發方法很自然地支持了把系統分解成模塊的設計原則:對象就是模塊。它是把數據結構和操作這些數據的方法緊密地結合在一起所構成的模塊。
   2.抽象
   面向對象方法不僅支持過程抽象,而且支持數據抽象。
   3.信息隱藏
   在面向對象方法中,信息隱藏通過對象的封裝性來實現。
   4.低耦合
   在面向對象方法中,對象是最基本的模塊,因此,耦合主要指不同對象之間相互關聯的緊密程度。低耦合是設計的一個重要標准,因為這有助於使得系統中某一部分的變化對其它部分的影響降到最低程度。
   5.高內聚
   (1)操作內聚。
   (2)類內聚。
   (3)一般——具體內聚。

(二)面向對象設計的啟發規則

   1.設計結果應該清晰易懂
   使設計結果清晰、易懂、易讀是提高軟件可維護性和可重用性的重要措施。顯然,人們不會重用那些他們不理解的設計。
   要做到:
   (1)用詞一致。
   (2)使用已有的協議。
   (3)減少消息模式的數量。
   (4)避免模糊的定義。

   2.一般——具體結構的深度應適當

   3.設計簡單類
   應該盡量設計小而簡單的類,這樣便以開發和管理。為了保持簡單,應注意以下幾點:
   (1)避免包含過多的屬性。
   (2)有明確的定義。
   (3)盡量簡化對象之間的合作關系。
   (4)不要提供太多的操作。

   4.使用簡單的協議
   一般來說,消息中參數不要超過3個。

   5.使用簡單的操作
   面向對象設計出來的類中的操作通常都很小,一般只有35行源程序語句,可以用僅含一個動詞和一個賓語的簡單句子描述它的功能

   6.把設計變動減至最小
   通常,設計的質量越高,設計結果保持不變的時間也越長。即使出現必須修改設計的情況,也應該使修改的范圍盡可能小。
(三)系統設計

   系統設計是問題求解及建立解答的高級策略。必須制定解決問題的基本方法,系統的高層結構形式包括子系統的分解、它的固有並發性、子系統分配給硬軟件、數據存儲管理、資源協調、軟件控制實現、人機交互接口。

1.系統設計概述

    設計階段先從高層入手,然后細化。系統設計要決定整個結構及風格,這種結構為后面設計階段的更詳細策略的設計提供了基礎。

   (1)系統分解。
   系統中主要的組成部分稱為子系統,子系統既不是一個對象也不是一個功能,而是類、關聯、操作、事件和約束的集合。
   (2)確定並發性。
   分析模型、現實世界及硬件中不少對象均是並發的。
   (3)處理器及任務分配。
   各並發子系統必須分配給單個硬件單元,要么是一個一般的處理器,要么是一個具體的功能單元。
   (4)數據存儲管理。
   系統中的內部數據和外部數據的存儲管理是一項重要的任務。通常各數據存儲可以將數據結構、文件、數據庫組合在一起,不同數據存儲要在費用、訪問時間、容量及可靠性之間做出折衷考慮。
   (5)全局資源的處理。
   必須確定全局資源,並且制定訪問全局資源的策略。
   (6)選擇軟件控制機制。
   分析模型中所有交互行為都表示為對象之間的事件。系統設計必須從多種方法中選擇某種方法來實現軟件的控制。
   (7)人機交互接口設計。
   設計中的大部分工作都與穩定的狀態行為有關,但必須考慮用戶使用系統的交互接口。

   2.系統結構的一般框架

   3.系統分解——建立系統的體系結構
   可用的軟件庫以及程序員的編程經驗。
   通過面向對象分析得到的問題域精確模型,為設計體系結構奠定了良好的基礎,建立了完整的框架。

   4.選擇軟件控制機制
   軟件系統中存在兩種控制流,外部控制流和內部控制流。

   5.數據存儲管理
   數據存儲管理是系統存儲或檢索對象的基本設施,它建立在某種數據存儲管理系統之上,並且隔離了數據存儲管理模式的影響。

   6.設計人機交互接口
   在面向對象分析過程中,已經對用戶界面需求作了初步分析,在面向對象設計過程中,則應該對系統的人機交互接口進行詳細設計,以確定人機交互的細節,其中包括指定窗口和報表的形式、設計命令層次等項內容。

(四)對象設計
   1.對象設計概述
   2.三種模型的結合
   (1)獲得操作。
   (2)確定操作的目標對象。
   3.算法設計 
   4.優化設計
   5.控制的實現
   6.調整繼承
   7.關聯的設計
                                
八、面向對象的實現

(一)程序設計語言

   1.選擇面向對象語言
   采用面向對象方法開發軟件的基本目的和主要優點是通過重用提高軟件的生產率。因此,應該優先選用能夠最完善、最准確地表達問題域語義的面向對象語言。

   在選擇編程語言時,應該考慮的其他因素還有:對用戶學習面向對象分析、設計和編碼技術所能提供的培訓操作;在使用這個面向對象語言期間能提供的技術支持;能提供給開發人員使用的開發工具、開發平台,對機器性能和內存的需求,集成已有軟件的容易程度。

   2.程序設計風格
   (1)提高重用性。
   (2)提高可擴充性。
   (3)提高健壯性。

(二)類的實現

   在開發過程中,類的實現是核心問題。在用面向對象風格所寫的系統中,所有的數據都被封裝在類的實例中。而整個程序則被封裝在一個更高級的類中。在使用既存部件的面向對象系統中,可以只花費少量時間和工作量來實現軟件。只要增加類的實例,開發少量的新類和實現各個對象之間互相通信的操作,就能建立需要的軟件。

   一種方案是先開發一個比較小、比較簡單的來,作為開發比較大、比較復雜的類的基礎。

   (1)“原封不動重用。
   (2)進化性重用。
   一個能夠完全符合要求特性的類可能並不存在。
   (3)“廢棄性開發。
   不用任何重用來開發一個新類。
   (4)錯誤處理。
   一個類應是自主的,有責任定位和報告錯誤。

(三)應用系統的實現

   應用系統的實現是在所有的類都被實現之后的事。實現一個系統是一個比用過程性方法更簡單、更簡短的過程。有些實例將在其他類的初始化過程中使用。而其余的則必須用某種主過程顯式地加以說明,或者當作系統最高層的類的表示的一部分。

   C++C中有一個main( )函數,可以使用這個過程來說明構成系統主要對象的那些類的實例。

(四)面向對象測試

   (1)算法層。
   (2)類層。
   測試封裝在同一個類中的所有方法和屬性之間的相互作用。
   (3)模板層。
   測試一組協同工作的類之間的相互作用。
   (4)系統層。
   把各個子系統組裝成完整的面向對象軟件系統,在組裝過程中同時進行測試。 

九、面向對象和基於對象的區別

      很多人沒有區分面向對象基於對象兩個不同的概念。面向對象的三大特點(封裝,繼承,多態)卻一不可。通常基於對象是使用對象,但是無法利用現有的對象模板產生新的對象類型,繼而產生新的對象,也就是說基於對象沒有繼承的特點。而多態表示為父類類型的子類對象實例,沒有了繼承的概念也就無從談論多態。現在的很多流行技術都是基於對象的,它們使用一些封裝好的對象,調用對象的方法,設置對象的屬性。但是它們無法讓程序員派生新對象類型。他們只能使用現有對象的方法和屬性。所以當你判斷一個新的技術是否是面向對象的時候,通常可以使用后兩個特性來加以判斷。面向對象基於對象” 都實現了封裝的概念,但是面向對象實現了繼承和多態,而基於對象沒有實現這些,的確很饒口。 

      從事面向對象編程的人按照分工來說,可以分為類庫的創建者類庫的使用者。使用類庫的人並不都是具備了面向對象思想的人,通常知道如何繼承和派生新對象就可以使用類庫了,然而我們的思維並沒有真正的轉過來,使用類庫只是在形式上是面向對象,而實質上只是庫函數的一種擴展。 

      面向對象是一種思想,是我們考慮事情的方法,通常表現為我們是將問題的解決按照過程方式來解決呢,還是將問題抽象為一個對象來解決它。很多情況下,我們會不知不覺的按照過程方式來解決它,而不是考慮將要解決問題抽象為對象去解決它。有些人打着面向對象的幌子,干着過程編程的勾當。 

命令模式(Command Pattern) 

耦合與變化:
    耦合是軟件不能抵御變化災難的根本性原因。不僅實體對象與實體對象之間存在耦合關系,實體對象與行為操作之間也存在耦合關系。                                                                                             
動機(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角色的類,這其實相當於一個中間角色,前面我曾經說過,使用這樣的一個中間層也是我們經常使用的手法,即把AB的依賴轉換為AC的依賴。如下:
          

 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.可以容易地實現對請求的UndoRedo

    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 }

 

迭代器模式(Iterator Pattern) 

動機(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 }

.NETIterator中的應用:
在.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

3 {
4     IEumerator GetEnumerator();
5 }

Iterator實現要點:

1.迭代抽象:訪問一個聚合對象的內容而無需暴露它的內部表示。

2.迭代多態:為遍歷不同的集合結構提供一個統一的接口,從而支持同樣的算法在不同的集合結構上進行操作。

3.迭代器的健壯性考慮:遍歷的同時更改迭代器所在的集合結構,會導致問題。


vs2005快捷鍵 

 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: 全部變為小寫 

 

觀察者模式(Observer Pattern 

動機(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類不再依賴於具體的EmailerMobile,而是依賴於抽象的IObserverAccount。存在着的一個問題是EmailerMobile仍然依賴於具體的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         { }

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接口更為松耦合的設計。


解釋器模式(Interpreter Pattern) 

動機(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模式會產生比較大的類層次結構,需要求助於語法分析生成器這樣的標准工具。

中介者模式(Mediator Pattern) 

依賴關系的轉化:         

 

動機(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.如果請求傳遞到職責鏈的未尾仍得不到處理,應該有一個合理的缺省機制。這也是每一個接受對象的責任,而不是發出請求的對象的責任。

 

備忘錄模式(Memento Pattern) 

對象狀態的回溯:
    對象狀態的變化無端,如何回溯/恢復對象在某個點的狀態?
                        
動機:
    在軟件構建過程中,某些對象的狀態在轉換過程中,可能由於某種需要,要求程序能夠回溯到對象之前處於某個點時的狀態。如果使用一些公有接口來讓其他對象得到對象的狀態,便會暴露對象的細節實現。
    如何實現對象狀態的良好保存與恢復?但同時又不會因此而破壞對象本身的封裝性。
意圖:
在不破壞封裝性的前提下,捕獲一個對象的內部狀態,並在該對象之外保存這個狀態。這樣以后可以將該對象恢復到原先保存的狀態。

          
適用性:
    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模式。

 

策略模式(Strategy Pattern) 

算法與對象的耦合:
    對象可能經常需要使用多種不同的算法,但是如果變化頻繁,會將類型變得脆弱...
            
動機:
    在軟件構建過程中,某些對象使用的算法可能多種多樣,經常改變,如果將這些算法都編碼對象中,將會使對象變得異常復雜;而且有時候支持不使用的算法也是一個性能負擔。
    如何在運行時根據需要透明地更改對象的算法?將算法與對象本身解耦,從而避免上述問題?
意圖:
    定義一系統的算法,把它們一個個封裝起來,並且使它們可相互替換。本模式使得算法可獨立於使用它的客戶而變化。
                                                                     --------《設計模式》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     }

    由此可見,由於客戶端新增調用方式的選擇,就會修改SortTypeSort里的判斷語句。在類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對象,從而節省對象開銷。 

 

訪問者模式(Visitor Pattern) 

類層次結構的變化:
    類層次結構中可能經常由於引入新的操作,從而將類型變得脆弱...
                           
動機:
    在軟件構建過程中,由於需求的改變,某些類層次結構中常常需要增加新的行為(方法),如果直接在基類中做這樣的更改,將會給子類帶來很繁重的變更負擔,甚至破壞原有設計。
    如何在不更改類層次結構的前提下,在運行時根據需要透明地為類層次結構上的各個類動態添加新的操作,從而避免上述問題?
意圖:
    表示一個作用於某對象結構中的各元素的操作。它使你可以在不改變各元素的類的前提下定義作用於這引起元素的新操作。
結構:
             
適用性:
    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"類層次結構穩定,而其中的操作卻經常面臨頻繁改動". 

 

 

狀態模式(State Pattern) 

對象狀態影響對象行為:
    對象擁有不同的狀態,往往會行使不同的行為...
                   
動機:
    在軟件構建過程中,某些對象的狀態如果改變以及其行為也會隨之而發生變化,比如文檔處於只讀狀態,其支持的行為和讀寫狀態支持的行為就可能完全不同。
    如何在運行時根據對象的狀態來透明更改對象的行為?而不會為對象操作和狀態轉化之間引入緊耦合?
意圖:
 允許一個對象在其內部狀態改變時改變它的行為。從而使對象看起來似乎修改了其行為。  ------《設計模式》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對象,從而節省對象開銷。 

 

 

1.2.6 111)

 

1.2.7 111)

 

1.2.8 111)

 

1.2.9 111)

 

1.2.10 111)

 

1.2.11 111)

 

1.2.12 111)

 

1.2.13 111)

 

1.2.14 111)

 

1.2.15 111)

 

1.2.16 111)

 

1.2.17 111)

 

1.2.18 111)

 

1.2.19 111)

1.2.20 
111)

1.2.21 
111)

1.2.22 
111)

 

1.2.23 111)

阿斯頓法國紅酒看來


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM