设计模式学习之Facade模式

今天讲外观模式,这个模式其实理解起来超级简单,为什么呢?这个模式其实我觉得可以用四个字来形容它:化繁为简。因为这个模式就是用来提供简化接口的。什么是简化接口?其实就是将很多复杂的接口组合起来成为一个新接口。你或许会问,把许多复杂的接口组合起来不是会更复杂吗,怎么会变得简单,这不是自相矛盾吗?哈哈,那你就错了,其实外观模式更像一个东西:宏。又拿宏说事儿了,不是上次命令模式里面提到过“命令宏”的吗,这个模式和宏有什么关系呢?嘿嘿,如果把上次“命令宏”称为“狭义的宏”的话,那么外观模式就是“广义的宏”(似乎越来越难理解了……)。

别急,让我慢慢道来,先解决为什么叫“广义的宏”这个问题。首先,广义代表这个模式具有很高的通用性,也就是说,它不只是限于对单一类型的接口进行“捆绑销售”,它还能对不同类型的接口进行“集中甩卖”,哎,说的专业点,就是外观模式可以将不同子系统的接口组合成一个接口供用户使用,而这个接口因为组合了很多的功能,使得用户不用自己去定义n多的子系统,然后自己手动操作这些子系统的方法,而只需要调用这个合成的接口就能完成一大堆的功能,是不是简化了呢?

再来详细讲一下吧,定义还是必须的:

外观模式:
为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。

适用性:

  1. 当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade 可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过facade层。
  2. 客户程序与抽象类的实现部分之间存在着很大的依赖性。引入facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。
  3. 当你需要构建一个层次结构的子系统时,使用facade模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,你可以让它们仅通过facade进行通讯,从而简化了它们之间的依赖关系。

再上类图:

facademode

对这类图是不是有点无语?是不是感觉摸不着头脑?呵呵,别被它的外表所吓倒,其实你只需要关心最上面的那个接口就行了,对于subsystem也就是子系统可以完全不需要仔细的考虑,而只需要知道如何调用子系统提供的接口就行了,我们还是用个例子来说吧:

就拿开车来说,开车是需要很多的操作的,很多小细节啊(这可是学驾照时的亲身体会啊),我们简单说一下:开车门->插钥匙->启动发动机->踩离合器->进档->松离合器->松手刹->加油门->转动方向盘…(车跑起来了,哈哈,不过后序步骤就要靠你自己了;))。但随着科技的发展,现在果断出现了全自动的汽车(科技真给力),对于这种全自动的汽车来说,显然没有了人,这些操作就只能又计算机控制了,如果现在要你写一个这样的全自动驾车的程序,使得用户只需要插入钥匙,点击开车按钮就能自动开车了(这就方便了,每个人都会),那该怎么写呢?

是不是觉得很复杂?没关系,让复杂的问题交给科学家吧,咱们要懂得利用他们的成果,嘻嘻,假设科学家们已经将复杂的控制功能封装好了,成为了一个个子系统,比如车门,档位,发动机,离合器,手刹,油门都有了各自的控制方法,那我们现在就只需要写汽车的接口,就三个:权限认证(插钥匙,当然现在科技这么发达,也有可能出现别的验证方式呢),还有开车,停车,怎么做呢:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//汽车的接口
public interface ICar
{
public bool auth();
public void drive();
public void stop();
}
如前所述,现在科学家提供了很多的控制类接口:
//车门接口
public interface ICarDoor
{
public void open();
public void close();
}
//档位接口
public interface IGears
{
public void setLevel(int level);
}
//转向灯接口
public interface ITurnLight
{
//0为左,1为右
public void setLight(int direction);
}
//喇叭接口
public interface ISpeaker
{
public void beep();
}
//引擎接口
public interface IEngine
{
public void open();
public void close();
public void refuel();
}
//离合器接口
public interface IClutch
{
public void press();
public void loose();
}
//手刹接口
public interface IHandBrake
{
public void pull();
public void loose();
}
//油门接口
public interface IAccelerator
{
public void accelerate(int level);
}
//方向盘
public interface IWheel
{
public void toLeft(int degree);
public void toRight(int degree);
}
//权限认证(插钥匙,当然现在科技这么发达,也有可能出现别的验证方式呢),
public interface IAuthSystem
{
public bool auth();
}

既然已经有了这么多接口,那么我们的自动系统就只需要根据这些接口进行顺序的调用就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class AutoCar implements ICar
{
ICarDoor carDoor;
IGears gears;
ITurnLight turnLight;
ISpeaker speaker;
IEngine engine;
IClutch clutch;
IHandBrake handBrake;
IAccelerator accelerator;
IWheel wheel;
public AutoCar(ICarDoor carDoor, IGears gears, ITurnLight turnLight, ISpeaker speaker, IEngine engine,
IClutch clutch, IHandBrake handBrake, IAccelerator accelerator, IWheel wheel, IAuthSystem authSystem)
{
this.carDoor=carDoor;
this.gears=gears;
this.turnLight=turnLight;
this.speaker=speaker;
this.engine=engine;
this.clutch=clutch;
this.handBrake=handBrake;
this.accelerator=accelerator;
this.wheel=wheel;
this.authSystem=authSystem;
}
public void drive()
{
carDoor.close();//关门都由系统来做,人真懒啊,:)
if(authSystem.auth())//验证权限
{
engine.open();//打开发动机
clutch.press();//踩死离合器
gears.setLevel(1);//进1档
turnLight.setLight(0);//开左转向灯
speaker.beep();//鸣笛
clutch.loose();//松开离合器
handBrake.loose();//松手刹
accelerator.accelerate(1);//加一点就行了,别过猛了,不然会很暴力的,:)
//后面的事交给科学家吧,:)
}
}
public void stop();
}

这样一来,用户只需要关心ICar接口就行了,想想当你想出门的时候,只需要打开车门,插入钥匙,然后按“开车”按钮,然后什么都不用管,那将是多么的惬意啊,其实,这一切都得感谢外观模式啊,外观模式就是提供一个非常友好的外观给用户(长得漂亮谁不喜欢啊:)),使得用户不需要关心它内部的实现细节,使用户从繁重的工作中解脱出来。

唉,这上面就是外观模式?不就是把一堆类集合在一起,然后把需要的不同类的方法集中到一个方法里面实现吗,这么简单?是啊,就这么简单!多简单啊!我想现在你应该搞清楚为什么我把外观模式叫做广义的宏了吧,不信你再看看上面的代码,Car类中的drive()方法不就是个典型的宏吗,如果你还不清楚,请深入的学一下office word,:)

好了,这个模式就讲到这里,但顺便提一下另外几个模式:装饰者模式和适配器模式,难道这三个模式有什么共同点吗?是的,它们的共同点就是:对接口进行再次封装。装饰者模式是将被装饰者的接口动态的加上新的职责,适配器模式是将被适配者的接口转换成新的接口,而外观模式则是组合多个系统的接口创造新的接口。各种模式都有各自独特的作用,要看你怎么用了。

设计模式学习之Adapter模式

今天要说适配器模式了,适配器模式其实非常好理解,因为现实中存在很多不同种类的适配器,像usb转串口线就是讲usb接口和串口进行转换,VGA转HDMI线就是讲VGA接口和HDMI接口进行转换,这些都是硬件层次的,那么软件层次的就更好举例了,比如现在有某些usb设备,像usb无线键盘/鼠标啊,usb网卡等等如果直接插到电脑上是不会工作的,必须在电脑端还要装上相应的驱动程序,那么这个驱动程序其实就是一个软件适配器,将usb设备转换成为操作系统能够识别的设备,其实,usb设备远不止这些,到淘宝一查,我来个去,真是什么都有啊:

taobaousb

其实这些设备不是都有适配器的,有很多只用使用usb进行供电,把usb当电源了,但有些数码设备是确实有适配器的,很简单的就是手机的充电线,现在的智能手机都有一根充电线和一个充电器,充电线的一头是连接手机的,另一头则是一个usb接口,这条线连接到充电器就能给手机充电,连接到电脑就能使电脑和手机之间传输数据。

其实很早以前,就连鼠标键盘这种最常见的设备,都不是用的usb接口,而是用的PS/2接口,但现在支持usb接口的设备是越来越多啊,姑且不论usb到底为什么这么受欢迎(其实就是方便 :) ),我们只看这么多设备是如何做到支持usb接口的,难道说他们都会升级固件?那恐怕不尽然,除非是在usb接口普及之后所生产的新产品,否则那些旧产品要支持新的接口是不现实的,因为是硬件,厂商不可能回收后重做再给用户啊,那多亏啊。那这么说,那些旧产品就不能使用usb接口了啊,那usb接口怎么可能普及的这么快呢?答案是:适配器,不管是硬件的,还是软件的(驱动程序),都能

使采用旧接口的设备使用新的接口。

就比如usb网卡,一个usb网卡其实内部包含了一个usb接口与网卡的转接器,而在电脑端也有相应的网卡驱动让系统识别这是个网卡,它就有硬件和软件两个适配器,当用户需要从网络中请求数据时,操作系统会将请求编码后通过驱动程序从usb接口转发给网卡,网卡中的适配器对收到的请求进行解码后发送给网卡处理,网卡从互联网获得数据后将数据通过网卡适配器进行编码后通过usb接口发给电脑,电脑端操作系统通过驱动程序将数据再次解码后交给用户,这是不是有些复杂?

哎,扯了这么多,还没进入正题呢,既然适配器适用于不同接口之间的转换,那么在设计模式中是如何做到的呢?
还是照本宣科,先把定义摆出来:

适配器模式:
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

适用性:

  1. 你想使用一个已经存在的类,而它的接口不符合你的需求。
  2. 你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
  3. (仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。

定义总是概括性太强,不适合理解啊,有点懵了吧,下面来看个例子吧,就拿usb网卡来说,比如要做一个usb网卡驱动,操作系统提供的网卡API为:

1
2
3
4
5
6
public interface INetCard
{
public Host connect(IPAddr ip);
public NetData getData(Host host);
public void sendData(Host host, NetData data);
}

而usb接口提供的API为:

1
2
3
4
5
public interface IUsb
{
public UsbData getData();
public sendData(UsbData data);
}

网卡端的解码的方式为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class NetCardDecoder
{
private UsbData data;
public void NetCardDecoder(data)
{
this.data=data;
}
public void doWork(Operator operator)
{
operator.execute();
}
public Operator decode(UsbData data)
{
//对USB接口获取的数据进行解析后对不同的请求返回不同的操作对象
}
}

现在我们要做网卡驱动,所以需要将网卡接口适配成Usb接口,所以我们创造一个网卡适配器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//网卡数据格式与Usb数据格式的转换工具类
public class DataConvertorTool
{
//将UsbData转换NetData
public static NetData usbData2Net(UsbData data)
{
}
//将NetData转换UsbData
public static UsbData netData2Usb(NetData data)
{
}
}
//网卡适配器类
public class NetCardAdapter implements INetCard
{
private IUsb usb;
public class NetCardAdapter(IUsb usb)//将被适配者传给适配器
{
this.usb=usb;
}
//适配网卡接口
public Host connect(IPAddr ip)
{
UsbData data=new UsbData("connect");
data.addParam("ip", ip);
usb.sendData(data);
}
public NetData getData(Host host)
{
UsbData dataRequest=new UsbData("get");
data.addParam("host", host);
usb.sendData(data);
UsbData dataUsbReceive=usb.getData();
NetData dataNetReceive=DataConvertorTool.usbData2Net(dataUsbReceive);
return dataNetReceive;
}
public void sendData(Host host, NetData data)
{
UsbData dataRequest=new UsbData("send");
dataRequest.addParam("host", host);
dataRequest.addParam("data",data);
usb.sendData(data);
}
}

从上述代码可以看出,适配器通过实现用户接口,并通过组合被适配者接口来实现接口之间的转换,但上述代码中我们只是实现了适配器的一种方式,即用组合的方式实现适配,其实,适配器模式还能使用另外一种方式,即类的多继承,但java中无法实现多继承,所以无法实现,但java支持多接口的继承,所以我个人认为其实概念上也是可以实现的,如果使用第二种方式,可以这样来写适配器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//网卡适配器类
public class NetCardAdapter implements INetCard, IUsb
{
private IUsb usb;
public class NetCardAdapter(IUsb usb)//将被适配者传给适配器
{
this.usb=usb;
}
//适配网卡接口
public Host connect(IPAddr ip)
{
UsbData data=new UsbData("connect");
data.addParam("ip", ip);
usb.sendData(data);
}
public NetData getData(Host host)
{
UsbData dataRequest=new UsbData("get");
data.addParam("host", host);
usb.sendData(data);
UsbData dataUsbReceive=usb.getData();
NetData dataNetReceive=DataConvertorTool.usbData2Net(dataUsbReceive);
return dataNetReceive;
}
public void sendData(Host host, NetData data)
{
UsbData dataRequest=new UsbData("send");
dataRequest.addParam("host", host);
dataRequest.addParam("data",data);
usb.sendData(data);
}
//继承了Usb接口
public UsbData getData()
{
}
public sendData(UsbData data)
{
}
}

但由于只能继承接口,所以对于Usb接口方法还必须重新实现,显然是不现实的,因为这样就不能实现代码的复用和封闭了,自然是没有意义的,但若在c++或其他支持多继承的语言中,通过多继承被适配器类而不是接口可以方便的实现代码复用。

适配器模式与装饰者模式有些类似,它们都是结构型设计模式,然后都是对某个接口进行操作,但它们却有本质的不同:

首先, 适配器模式是将一个接口转换为另一个接口,而装饰者模式并不改变接口,只是在原实现上面动态的添加某种职责
其次, 它们的引用对象不一样,适配器模式引用被适配者(属于另一个接口),而装饰者模式引用自身(同一个接口)
最后, 它们的使用方式不一样,装饰者模式通过product=new ProductDecorator(product)来包装自身,而适配器模式通过product=new ProductAdapter(otherProduct)来像product一样使用otherProduct

设计模式学习之Factory Method模式和Abstract Factory模式

终于到了工厂模式了,说起工厂模式,不得不把工厂方法模式和抽象工厂模式结合起来说,这两种模式都有工厂,乍听起来还真容易混淆,但它们却是不相同的两种模式,但又互相有联系。那么这两者之间各有什么用途,互相之间又有什么联系呢?一个一个来吧。

既然说起了工厂模式,那么首先有一点是需要搞清的,那就是,所有的工厂模式都是为了将对象的创建过程封装起来,要么 将创建过程隔离出来 ,要么 将创建过程抽象成接口

那么,在说这两种模式之前,先说一下简单工厂,所谓的简单工厂,其实就是一种最简单的将创建过程隔离的设计方法。我们通常在创建一个对象时,都会用到如下语句:
Object o=new Object();

即new一个对象,但这种做法是最最基本最最原始的创建对象的方法,在面向对象的设计中,我们必须要遵循一个原则: 要依赖抽象,不要依赖具体类 。所以,这种做法因为太过具体,所以我们得想办法把它抽象出来,怎么抽象呢,用简单工厂可以这样实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Client
{
public void do()
{
Object o=SimpleFactory.createObject();
}
}
public class SimpleFactory
{
public static Object createObject()
{
Object o=new Object();
return o;
}
}

咋看起来变化不大,不就是吧new Object改为一个类的静态方法了吗(其实也可以用非静态方法,不过需要先实例化对象),但这样做却可以适应需求的改变,比如说,我现在想要根据不同的类型创建不同的对象,如果直接用new方法,则会在do方法里面写上很多歌new Object()的语句,而且还有很多的判断,每当新增加一种类型,就得改动Client类代码。而如果将创建对象的过程用简单工厂封装起来,则只需要改动简单工厂里面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//直接用new方法
public class Client
{
public void do(String type)
{
Object o;
switch(type)
{
case "type1":o=new Object1();break;//Object1、Object2都是Object的子类
case "type2":o=new Object2();break;
//...
}
}
}
//采用简单工厂封装创建过程
public class Client
{
public void do(String type)
{
Object o=SimpleFactory.createObject(type);
}
}
public class SimpleFactory
{
public static Object createObject(String type)
{
Object o;
switch(type)
{
case "type1":o=new Object1();break;//Object1、Object2都是Object的子类
case "type2":o=new Object2();break;
//...
}
return o;
}
}

但这样做还是会带来不便,因为还是要改动代码,增加新的判断分支,怎样才能更好的封装以增强可维护性和可扩展性呢,工厂方法模式和抽象工厂模式就可以大大增强简单工厂的作用。

那么首先说说工厂方法模式,先说基本概念吧

工厂方法模式:
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。

适用性:

  1. 当一个类不知道它所必须创建的对象的类的时候。
  2. 当一个类希望由它的子类来指定它所创建的对象的时候。
  3. 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

工厂方法模式中,最重要的一点是“让子类决定实例化那一个类”,这一点怎么才能做到呢,而且这样做有什么好处呢?

还是举例子最好了,对于工厂模式,能举的最好的例子当然是与生产产品有关的了,现在,我们假定生产一种手机,那么我们知道,每种手机都需要很多零部件,然后进行组装,打磨,包装等等,对于手机最主要的零件cpu来说,就有不同类型的cpu,比如高通,intel,苹果,德州仪器,或者mtk等等,那么我们在生产手机时,就需要使用某种芯片,就相当于创建这些对象。但不同手机可以使用不同的芯片,如果采用简单工厂来生产这些芯片,就像下面一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Cellphone
{
Cpu cpu;
Memory memory;
Screen screen;
public void getCpu();
public void makePhone(String phoneType)
{
switch(phoneType)
{
case "iphone4s":cpu=CpuFactory.createCpu("apple A5");break;
case "三星i9100":cpu=CpuFatory.createCpu("高通");break;
case "摩托罗拉me525":cpu=CpuFactory.createCpu("高通");break;
case "华为8500":cpu=CpuFactory.createCpu("德州仪器");break;
case "诺基亚":cpu=CpuFactory.createCpu("intel");break;
//...
}
}
public void package();
}
public class CpuFactory
{
public static Cpu createCpu(String cpuType)
{
Cpu cpu;
switch(cpuType)
{
case "apple A5":cpu=new AppleCpu("A5");break;
case "apple A5X":cpu=new AppleCpu("A5X");break;
case "高通1代":cpu=new QualcommCpu("1");break;
case "高通2代":cpu=new QualcommCpu("2");break;
case "德州仪器":cpu=new TiCpu();break;
case "intel":cpu=new IntelCpu();break;
//...
}
}
}

那么,如果增加新的cpu,就要在CpuFactory里面再增加新的判断分支。而如果使用工厂方法模式,就可以这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class Cellphone
{
protected String type;
protected Cpu cpu;
protected Memory memory;
protected Screen screen;
protected abstract Cpu getCpu();
public Cellphone(String type)
{
this.type=type;
}
public void makePhone()
{
cpu=getCpu();
//..
}
public void package();
}
public class Iphone extends Cellphone
{
public Cpu getCpu()
{
switch(type)
{
case "3gs":cpu=CpuFactory.createCpu("apple A3");
case "4":cpu=CpuFactory.createCpu("apple A5");
case "4s":cpu=CpuFactory.createCpu("apple A5X");
case "5":cpu=CpuFactory.createCpu("apple A6");
//...
}
}
}
public class Motorola extends Cellphone
{
public Cpu getCpu()
{
switch(type)
{
case "millstone":cpu=CpuFactory.createCpu("高通1代");break;
case "me525":cpu=CpuFactory.createCpu("高通2代");break;
case "millstone2":cpu=CpuFactory.createCpu("高通2代");break;
case "xt860":cpu=CpuFactory.createCpu("高通3代");break;
//...
}
}
}
//...
public class CpuFactory
{
public static Cpu createCpu(String cpuType)
{
Cpu cpu;
switch(cpuType)
{
case "apple A5":cpu=new AppleCpu("A5");break;
case "apple A5X":cpu=new AppleCpu("A5X");break;
case "高通1代":cpu=new QualcommCpu("1");break;
case "高通2代":cpu=new QualcommCpu("2");break;
case "德州仪器":cpu=new TiCpu();break;
case "intel":cpu=new IntelCpu();break;
//...
}
}
}

对于上例来说,我们将不同类型的手机分离了出来,让它们共同继承Cellphone基类,显得结构清晰,便于扩展,比如现在如果有一款三星的手机,就只需要继承新建一个Samsung类然后继承Cellphone类,然后重写getCpu方法即可实现三星手机的cpu采购过程。也就是说,getCpu作为一个抽象的创建cpu的方法,让子类去具体实现。

像这种结构,就体现了“具体的手机产品需要具体的cpu类型”的设计。也就是“定义一个用于创建对象的接口(getCpu),让子类决定实例化哪一个类”,在Iphone类和Motorola类中分别实现了getCpu方法,使用简单工厂产生了各种的cpu。在这里,并不是采用工厂方法模式取代简单工厂,而是将简单工厂也应用到了工厂方法模式当中,因为这两者是不同的设计思想,也即 工厂方法模式是将创建过程抽象成接口 ,而 简单工厂是将创建过程隔离出来 。而抽象工厂模式则是这两种思想的进一步结合和升华。

抽象工厂模式:
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

适用性:

  1. 一个系统要独立于它的产品的创建、组合和表示时。
  2. 一个系统要由多个产品系列中的一个来配置时。
  3. 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
  4. 当你提供一个产品类库,而只想显示它们的接口而不是实现时。

前面的例子虽然将不同的手机种类从判断分支中解脱了出来,但对于cpu的制作仍然显得比较乱,也就是说CpuFactory承担了所有的cpu制作任务(搞垄断啊:>),而cpu的型号五花八门,同一种型号的cpu也有很多的版本,如果放到一个工厂生产,实在不是一个聪明的做法,所以,我们自然考虑将不同的cpu从一个简单工厂解脱出来,分别由不同的工厂来生产(这样才有竞争嘛:>),因此,我们将简单工厂改造成抽象工厂,怎么改造?看下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//抽象工厂类
public class CpuFactory
{
Cpu cpu;
public abstract Cpu createCpu(String type);
}
//苹果cpu工厂类
public class AppleCpuFactory extends CpuFactory
{
public Cpu createCpu(String type)
{
switch(type)
{
case "A3":this.cpu=new AppleCpu("A3");break;
case "A5":this.cpu=new AppleCpu("A5");break;
case "A5X":this.cpu=new AppleCpu("A5X");break;
case "A6":this.cpu=new AppleCpu("A6");break;
//...
}
}
}
//高通cpu工厂类
public class QualcommCpuFactory extends CpuFactory
{
public Cpu createCpu(String type)
{
switch(type)
{
case "1代":this.cpu=new QualcommCpu("1");break;
case "2代":this.cpu=new QualcommCpu("2");break;
case "3代":this.cpu=new QualcommCpu("3");break;
//...
}
}
}
//德州仪器工厂类
public class TiCpuFactory extends CpuFactory
{
public Cpu createCpu(String type)
{
switch(type)
{
case "1代":this.cpu=new TiCpu("1");break;
case "2代":this.cpu=new TiCpu("2");break;
case "3代":this.cpu=new TiCpu("3");break;
//...
}
}
}
//cpu产品抽象类
public class Cpu
{
protected float frequency;
protected String type;
public Cpu(String type)
{
this.type=type;
}
}
//苹果cpu产品类
public class AppleCpu extends Cpu
{
}
//高通cpu产品类
public class QualcommCpu extends Cpu
{
}
//德州仪器cpu产品类
public class TiCpu extends Cpu
{
}

使用这种模式,就将各种具体的工厂分出了清晰的结构,也就是将工厂也抽象成接口了,这就是为什么叫“抽象工厂”的原因。
既然我们升级简单工厂为抽象工厂,那么创造产品自然就要用新的接口方法了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Cellphone
{
protected String type;
protected Cpu cpu;
protected Memory memory;
protected Screen screen;
protected CpuFactory cpuFactory;
protected abstract Cpu getCpu();
public Cellphone(String type)
{
this.type=type;
}
public void makePhone()
{
cpu=getCpu();
//..
}
public void package(){};
}
public class Iphone extends Cellphone
{
public Cpu getCpu()
{
this.cpuFactory=new AppleCpuFactory();//由于创建产品的方法不再是静态方法,所以需要创建实例
switch(type)
{
case "3gs":cpu=this.cpuFactory.createCpu("A3");
case "4":cpu=this.cpuFactory.createCpu("A5");
case "4s":cpu=this.cpuFactory.createCpu("A5X");
case "5":cpu=this.cpuFactory.createCpu("A6");
//...
}
}
}
public class Motorola extends Cellphone
{
public Cpu getCpu()
{
this.cpuFactory=new QualcommCpuFactory();
switch(type)
{
case "millstone":cpu=this.cpuFactory.createCpu("1");break;
case "me525":cpu=this.cpuFactory.createCpu("2");break;
case "millstone2":cpu=this.cpuFactory.createCpu("2");break;
case "xt860":cpu=this.cpuFactory.createCpu("3");break;
//...
}
}
}

这样一来,整个结构就清晰多了,用一句话概括就是:由具体的手机使用具体的工厂生产具体的cpu,但手机、工厂和cpu都通过接口进行交互,较好的封装了各自的实现,从而达到了较好的可扩展性,大大降低了各自的耦合性。现在,如果要添加新的手机、新的cpu,就可以建立新类型的工厂来实现对新的cpu的创建过程。

设计模式之Command模式

今天学习了命令模式,这个模式非常的给力,它能将命令封装起来让另外的执行者去执行,相当于一个命令的收集和转发过程,而且,这个模式还能将一系列的命令组合成“命令宏”,并且可以轻松的完成撤销的操作,非常适合日志系统或事务处理系统,也常用于窗口编程中的菜单命令处理,好了,下面回顾一下这个模式吧

命令模式:
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

适用性:

  1. 抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(callback)函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。Command 模式是回调机制的一个面向对象的替代品。
  2. 在不同的时刻指定、排列和执行请求。一个Command 对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
  3. 支持取消操作。Command 的Excute 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。Command 接口必须添加一个Unexecute 操作,该操作取消上一次Execute 调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用Unexecute 和Execute 来实现重数不限的“取消”和“重做”。
  4. 支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在Command 接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用Execute 操作重新执行它们。
  5. 用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( transaction )的信息系统中很常见。一个事务封装了对数据的一组变动。Command 模式提供了对事务进行建模的方法。Command 有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。

这个模式怎么举例呢?既然是命令模式,那么就找一些需要发送命令的场合吧,最常见的当然是软件中的菜单了,每一个菜单对应一个命令,当用户点击菜单的时候就会调用与菜单相关联的命令了,那么怎么来使用命令模式构建菜单呢,看代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//菜单接口
public interface IMenu
{
public Item getItem(int itemIndex);
public void addItem(Item item);
public int getItemLength();
public void setItem(int itemIndex, Command command);
public void isClicked(int itemIndex);
}
public class Menu implements IMenu
{
protected Item[] items;
protected int itemLength;
private String title;
public Menu(String title)
{
this.title=title;
}
public Item getItem(int itemIndex) throws OverflowException
{
if(itemIndex<0 || itemIndex>itemLength-1)
{
throw new OverflowException("out of range");
}
return items[itemIndex];
}
public void addItem(Item item)
{
if(item != NullItem)
{
items.add(item);
}
}
public void setItem(int itemIndex, ICommand command)
{
if(itemIndex<0 || itemIndex>itemLength-1)
{
throw new OverflowException("out of range");
}
Items[itemIndex].setCommand(command);
}
public void isClicked(int itemIndex)
{
this.getItem(itemIndex).isClicked();
}
}
//菜单项目
public class Item
{
private Command command;
private String title;
public Item(String title)
{
this.title=title;
}
public void setCommand(ICommand command)
{
this.command=command;
}
public void isClicked()
{
this.command.execute();
}
}
//命令接口
public interface ICommand
{
public void execute();
}
//保存命令
public class SaveCommand implements ICommand
{
Document document;
public SaveCommand(Document document)//document参数为接受者
{
this.document=document;
}
public void execute()
{
if(document.isModified())
{
document.save();
}
}
}
//打开命令
public class OpenCommand implements ICommand
{
Application application;
public OpenCommand(Application application)
{
this.application=application;
}
public void execute()
{
Document document=application.open();
application.addToWorkplace(document);
}
}
//退出命令
public class ExitCommand implements ICommand
{
Application application;
public ExitCommand(Application application)
{
this.application=application;
}
public void execute()
{
Document document;
for(document=Application.currentDocument(); document!=NullDocument; document=Application.nextDocument())
{
if(document.isModified())
{
document.alertSave("您还有修改过的文档尚未保存,是否保存?");
}
}
application.exit();
}
}
//剪切命令
public class CutCommand implements ICommand
{
SelectedBlock selectedBlock;
public CutCommand(SelectedBlock selectedBlock)
{
this.selectedBlock=selectedBlock;
}
public void execute()
{
System.Clipboard.save(selectedBlock);
selectedBlock.delete();
}
}
//粘贴命令
public class PasteCommand implements ICommand
{
Document document;
Object pasteObject
public PasteCommand(Document document, Object pasteObject)
{
this.document=document;
this.pasteObject=pasteObject;
}
public void execute()
{
document.insertAt(document.currentCursor(), pasteObject);
}
}
public class SaveAllCommand implements ICommand
{
Application application;
public SaveAllCommand(Application application)
{
this.application=application;
}
public void execute()
{
Document document;
for(document=application.currentDocument(); document!=NullDocument; document=application.nextDocument())
{
if(document.isModified())
{
document.save();
}
}
}
}
//客户代码
public class Client
{
public static void main(String[] argv)
{
/*
* 创建编辑器和新建文档
*/
Application application=new Application("文件编辑器");
Document document=application.new("命令模式日志");
application.addToWorkplace(document);
/*
* 创建菜单,项目,命令对象
*/
Menu menuFile=new Menu("文件菜单");
Menu menuEdit=new Menu("编辑菜单");
Item itemSave=new Item("保存");
Item itemOpen=new Item("打开");
Item itemExit=new Item("退出");
menuFile.addItem(itemSave);
menuFile.addItem(itemOpen);
menuFile.addItem(itemExit);
Item itemCut=new Item("剪切");
Item itemPaste=new Item("粘贴");
menuEdit.addItem(itemCut);
menuEdit.addItem(itemPaste);
ICommand commandSave=SaveCommand(document);
ICommand commandOpen=OpenCommand(application);
ICommand commandExit=ExitCommand(application);
ICommand commandCut=CutCommand(document.selectedBlock());
ICommand commandPaste=PasteCommand(document, System.clipboard.currentObject);
/*
* 赋予每个项目相应的命令
*/
menuFile.setCommand(0, commandSave);
menuFile.setCommand(1, commandOpen);
menuFile.setCommand(2, commandExit);
menuEdit.setCommand(0, commandCut);
menuEdit.setCommand(1, commandPaste);
/*
* 模拟菜单操作
*/
menuFile.isClicked(0);//点击保存
menuFile.isClicked(1);//点击打开
//用鼠标选中一行文字
menuEdit.isClicked(0);//点击剪切
//将光标移动到任意位置
menuEdit.isClicked(1);//点击粘贴
menuFile.isClicked(2);//点击退出
}
}

对于以上设计,是如何运用命令模式的呢?解释一下吧,首先,命令模式有一个公共的命令接口:

1
2
3
4
5
//命令接口
public interface ICommand
{
public void execute();
}

该接口只有一个方法:execute(),这个方法提供给调用者最为简洁的使用该命令的方式,用户根本不需要关心他发出的命令的具体实现细节,他只需要构造一个具体的命令对象并把命令的接收者告诉该对象,然后调用该命令的execute方法即可,命令对象自然心领神会的按照客户的要求将命令传达给接收者,接收者收到命令后具体的执行。在上述代码中,客户创建了菜单并往菜单中添加了具体的菜单项,紧接着又创建了各种命令对象并将命令对象和菜单项绑定到一起,即调用setCommand函数。在创建命令对象的时候,会根据不同的命令对象所需的接收者不同给他们传递不同的接收者对象,比如ExitCommand需要Application类型的接收者,而CutCommand需要SelectedBlock类型的接收者,所以分别传给他们对应类型的接收者对象,最后,由用户操作菜单,使得对应的被点击的菜单项的isClicked事件触发,从而触发对应命令的execute()方法来执行响应的命令。这就是整个命令模式的实现过程。

另外,命令模式其实还可以实现更多的功能,在上面的代码中,命令接口只有一个execute()方法,其实,还可以增加一个undo()方法来执行命令的撤销工作,如果实现了该方法,那么就能对对应的操作进行撤销操作了,此处就不详述了。

还有一个很给力的地方就是,命令可以组合!这是命令模式的精华所在,如果用过office的宏功能就会知道,一个宏可以一下子执行很多的命令,其实这种宏功能就是一种命令的组合,在定义的上述的命令模式后,完全可以定义一个宏命令,比如上面的例子中我想定义一个宏命令,操作次序如下:打开文档->粘贴->保存->退出程序(很无聊吧,:) ),就可以这样实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//宏命令
public class MacroCommand implements ICommand
{
ICommand[] commands;
public MacroCommand(ICommand[] commands)
{
this.commands=commands;
}
public void execute()
{
for(int i=0; i<commands.length, i++)
{
commands[i].execute();
}
}
}
//客户代码
public class Client
{
public static void main(String[] argv)
{
/*
* 创建编辑器和新建文档
*/
Application application=new Application("文件编辑器");
Document document=application.new("命令模式日志");
application.addToWorkplace(document);
/*
* 创建菜单,项目,命令对象
*/
...
ICommand commandSave=SaveCommand(document);
ICommand commandOpen=OpenCommand(application);
ICommand commandExit=ExitCommand(application);
ICommand commandCut=CutCommand(document.selectedBlock());
ICommand commandPaste=PasteCommand(document, System.clipboard.currentObject);
/*
* 赋予每个项目相应的命令
*/
...
/*
/ 模拟宏操作
*/
ICommand commands={commandOpen, commandPaste, commandSave, commandExit};
ICommand macroCommand=MacroCommand(commands);
macroCommand.execute();
}
}

是不是很酷?:)

设计模式学习之Decorator模式

继续学习设计模式,今天是装饰者模式,它属于结构型模式,首先还是它的基本概念:

装饰者模式:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。

适用性:

  1. 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
  2. 处理那些可以撤消的职责。
  3. 当不能采用生成子类的方法进行扩充时。一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

我的理解是:
装饰者模式就好比一个个wrapper(包装器),将一个对象一层层的包装,然后返回包装后的对象,就好比你在网上购买了一个电饭锅,那么这个电饭锅就是最原始的对象,而商家可能会在发货时进行一个初步包装,首先将电饭锅用一层泡沫包起来,然后装入一个纸箱,接着交给快递公司,然后快递公司收到这个物品后,还要进行二次包装,即使用快递公司专门的盒子装起来(或者用麻袋装好),然后贴上胶布开始运输,客户收到的这个箱子自后,一层层解开包装,最后拿出电饭锅。那么所有包装这个电饭锅的东西,像泡沫啊,纸箱啊,盒子啊,胶布啊,都可以算作装饰者,装饰者将原来的对象装饰完之后,会生成一个装饰后的对象,使源对象加上各种装饰品。所以,既然装饰者依赖于被装饰的对象,那么,装饰者在实现时就理所当然需要一个被装饰的对象的引用了,然后装饰完之后该引用就会指向装饰后的对象了。

所以,装饰者模式一定要搞明白装饰者的使用方式。

另外,因为装饰者能够替换源对象,所以在实际实现时需要将装饰者和源对象继承同一个父类(这个和实际有所不同,实际中,装饰者和源对象是不同的对象种类,但设计总归设计,不能总是和实际完美的相符,是需要一些灵活的处理的,而且这种设计的其本质是装饰者装饰之后的对象和源对象属于同一个父类,就好比电饭锅和装在盒子里的电饭锅都是电饭锅一样,但盒子并不是电饭锅)

还有一点就是,实际实现的装饰者,是装饰源对象的某个方法,相当于将被装饰的对象的某个方法增添或替换成某种方法而不改变源对象的代码和结构,就相当于动态的改变某种功能。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//被装饰者接口
public interface IProduct
{
public void transport();
}
//具体的被装饰者
public class Product implements IProduct
{
protected String name;
public Product(String name)
{
this.name=name;
}
public void transport()
{
System.out.println("原始的产品:"+this.name);
System.out.println("运输...");
}
}
//装饰者类
public class Decorator implements IProduct
{
protected IProduct product;
protected String name;
public Decorator(IProduct product, String name)
{
this.product=product;
this.name=name;
}
public void transport()
{
System.out.println("包装");
if(this.product != NULL)
{
this.product.transport();
}
}
}
//具体的装饰者1
public class Decorator1 extends Decorator
{
public void Decorator1(IProduct product)
{
this.product=product;
this.name="泡沫";
}
public void transport()
{
System.out.println("用"+this.name+"包装");
if(this.pruduct != NULL)
{
this.product.transport();
}
}
}
//具体的装饰者2
public class Decorator2 extends Decorator
{
public void Decorator1(IProduct product)
{
this.product=product;
this.name="纸盒";
}
public void transport()
{
System.out.println("用"+this.name+"包装");
if(pruduct != NULL)
{
this.product.method1();
}
}
}
public class Test
{
public static void main(String argv[])
{
IProduct product=new Product("电饭锅");
product=new Decorator1(product);//用泡沫包装
product=new Decorator2(product);//用纸盒包装
/*之后product对象就是被纸盒包装又被泡沫包装的对象了*/
product.transport();//从外到里依次输出包装物直到原产品,最后进行运输
}
}

设计模式学习之Observer模式

今天继续学习设计模式。学到的两个模式:观察者模式,它属于行为性模式。

观察者模式:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。

适用性:

  1. 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
  2. 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
  3. 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

我的理解是:
观察者模式首先定义了一个被观察者,然后让很多的观察者订阅被观察者,而被观察者在自己的状态有了更新时向所有的观察者“推送”自己的状态信息,从而使所有的观察者都得到通知。其实这个模式让人很容易想到当前一种最为潮流的互联网应用:微博。就拿微博为例,每一个人都可以是观察者与被观察者两种身份,当你关注别人的时候,比如你的一个朋友小罗,你就成为了小罗的粉丝,就相当于此时小罗为被观察者,而你自己是观察者身份,你订阅了被观察者,使被观察者在自己状态发生改变时会及时通知你。比如小罗今天去旅游,他将照片发到了微博上,这样你就能及时的看到他的动态了。而且不只是你,他所有的粉丝都能看到他的动态了。而当你的另外一个朋友,比如小杨关注了你,这样你就成为了被观察者,而小杨就是观察者,当你在微博上的个人中心上发状态时,小杨就能实时看到你的状态了,或者会对你的状态进行评论。所以,Observer模式很容易理解。

那么,如何来实现它呢?其实很简单,主要是在被观察者上面做文章,被观察者类需要引入三个方法:AddObserver, DeleteObserver, Notify。
前两个方法用来动态的增加和删除观察者,后面一个方法用来通知所有的观察者。
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//被观察者接口
public interface IObservarable
{
public void addObserver(Observer obs);
public void deleteObserver(Observer obs);
public void notify();
}
//被观察者类
public class Observarable implements IObserverable
{
protected ArrayList arrObservers;//存放所有的观察者
protected ArrayList arrStates;//存放自己的状态以便通知所有的观察者
protected String name;
public Observarable(String name)
{
this.name=name;
this.arrObservers=new ArrayList();
}
public void addObserver(Observer obs)
{
arrObservers.add(obs);
}
public void deleteObserver(Observer obs)
{
int i=this.arrObservers.indexof(obs);
if(i>=0)
{
this.arrObservers.remove(i);
}
}
public void notify()
{
this.arrStates=new ArrayList();
this.arrStates.add(this.name);
for(int i=0; i<this.arrObservers.size(); i++)
{
Observer obs=arrObservers.get(i);
obs.update(this, this.arrStates);
}
}
public string getName()
{
return this.name;
}
}
//更加具体的被观察者(拥有自己的状态)
public class Observerable1 extends Observable
{
private State1 state1;
private State2 state2;
public void notify()
{
this.arrStates.add(this.name);
this.arrStates.add(this.state1);
this.arrStates.add(this.state2);
for(int i=0; i<this.arrObservers.size(); i++)
{
Observer obs=arrObservers.get(i);
obs.update(this, arrStates);
}
}
public State1 getState1()
{
return this.state1;
}
public State2 getState2()
{
return this.state2;
}
}
//观察者接口
public interface IObserver
{
public void update(Observerable o, Object arg);
}
//观察者类
public class Observer implements IObserver
{
protected String name;
public Observer(String name)
{
this.name=name;
}
public void update(Observerable o, Object arg)
{
System.out.println("Observer "+this.name+" has been notified!");
}
}
//更加具体的观察者1(采用拉的方式)
public class Observer1 extends Observer
{
public void update(Observerable o, Object arg)
{
super.update();
System.out.println("I love you!");
System.out.println("I have get the state of "+o.getName());
if(o instanceof Observerable1)
{
System.out.println("state1:"+o.getState1()+", state2:"+o.getState2());
}
}
}
//更加具体的观察者2(采用推的方式)
public class Observer2 extends Observer
{
public void update(Observerable o, Object arg)
{
super.update();
System.out.println("I love you!");
if(o instanceof Observerable1)
{
System.out.println("I have get the state of "+arg[0].toString());
System.out.println("state1:"+(State1)arg[1].toString()+", state2:"+(State2)arg[2].toString());
}
}
}
public class Test
{
public static void main(string []argv)
{
Observer1 obs1=new Observer1("xiaoluo");
Observer1 obs2=new Observer2("xiaoyang");
Observerable1 obsb1=new Observerable1("xiaoguo");
obsb1.addObserver(obs1);
obsb1.addObserver(obs2);
obsb1.notify();
}
}