常用设计模式——总览

2016-07-12 10:22:08来源:oschina作者:凡尘里的一根葱人点击

1.单例模式

Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。”


目的:是使内存中保持1个对象。


单例模式三种常用形式:


第一种形式:懒汉式,也是常用的形式。


publicclassSingletonClass{
privatestaticSingletonClassinstance=null;
publicstaticSingletonClassgetInstance()
{
if(instance==null)
{
instance=newSingletonClass();
}
returninstance;
}
privateSingletonClass(){
}
}

第二种形式:饿汉式


//对第一行static的一些解释
//java允许我们在一个类里面定义静态类。比如内部类(nestedclass)。
//把nestedclass封闭起来的类叫外部类。
//在java中,我们不能用static修饰顶级类(toplevelclass)。
//只有内部类可以为static。
publicstaticclassSingleton{
//在自己内部定义自己的一个实例,只供内部调用
privatestaticfinalSingletoninstance=newSingleton();
privateSingleton(){
//dosomething
}
//这里提供了一个供外部访问本class的静态方法,可以直接访问
publicstaticSingletongetInstance(){
returninstance;
}
}

第三种形式: 双重锁的形式。


publicstaticclassSingleton{
privatestaticSingletoninstance=null;
privateSingleton(){
//dosomething
}
publicstaticSingletongetInstance(){
if(instance==null){
synchronized(Singleton.class){
if(null==instance){
instance=newSingleton();
}
}
}
returninstance;
}
}


2.简单工厂模式

简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。


Output,接口


publicinterfaceOutput
{
//接口里定义的属性只能是常量
intMAX_CACHE_LINE=50;
//接口里定义的只能是public的抽象实例方法
voidout();
voidgetData(Stringmsg);
}

Printer,Output的一个实现


publicclassPrinterimplementsOutput
{
privateString[]printData=newString[MAX_CACHE_LINE];
//用以记录当前需打印的作业数
privateintdataNum=0;
publicvoidout()
{
//只要还有作业,继续打印
while(dataNum>0)
{
System.out.println("打印机打印:"+printData[0]);
//把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData,1,printData,0,--dataNum);
}
}
publicvoidgetData(Stringmsg)
{
if(dataNum>=MAX_CACHE_LINE)
{
System.out.println("输出队列已满,添加失败");
}
else
{
//把打印数据添加到队列里,已保存数据的数量加1。
printData[dataNum++]=msg;
}
}
}

BetterPrinter,Output的一个实现


publicclassBetterPrinterimplementsOutput
{
privateString[]printData=newString[MAX_CACHE_LINE*2];
//用以记录当前需打印的作业数
privateintdataNum=0;
publicvoidout()
{
//只要还有作业,继续打印
while(dataNum>0)
{
System.out.println("高速打印机正在打印:"+printData[0]);
//把作业队列整体前移一位,并将剩下的作业数减1
System.arraycopy(printData,1,printData,0,--dataNum);
}
}
publicvoidgetData(Stringmsg)
{
if(dataNum>=MAX_CACHE_LINE*2)
{
System.out.println("输出队列已满,添加失败");
}
else
{
//把打印数据添加到队列里,已保存数据的数量加1。
printData[dataNum++]=msg;
}
}
}

OutputFactory,简单工厂类


publicclassOutput{
publicOutputgetPrinterOutput(Stringtype){
if(type.equalsIgnoreCase("better")){
returnnewBetterPrinter();
}else{
returnnewPrinter();
}
}
}
publicclassComputer
{
privateOutputout;
publicComputer(Outputout)
{
this.out=out;
}
//定义一个模拟获取字符串输入的方法
publicvoidkeyIn(Stringmsg)
{
out.getData(msg);
}
//定义一个模拟打印的方法
publicvoidprint()
{
out.out();
}
publicstaticvoidmain(String[]args)
{
//创建OutputFactory
OutputFactoryof=newOutputFactory();
//将Output对象传入,创建Computer对象
Computerc=newComputer(of.getPrinterOutput("normal"));
c.keyIn("建筑永恒之道");
c.keyIn("建筑模式语言");
c.print();c=newComputer(of.getPrinterOutput("better"));
c.keyIn("建筑永恒之道");
c.keyIn("建筑模式语言");
c.print();
}


3.建造模式

该模式其实就是说,一个对象的组成可能有很多其他的对象一起组成的,比如说,一个对象的实现非常复杂,有很多的属性,而这些属性又是其他对象的引用,可能这些对象的引用又包括很多的对象引用。封装这些复杂性,就可以使用建造模式。



4.门面模式

随着系统的不断改进和开发,它们会变得越来越复杂,系统会生成大量的类,这使得程序流程更难被理解。门面模式可为这些类提供一个简化的接口,从而简化访问这些类的复杂性。


门面模式(Facade)也被称为正面模式、外观模式,这种模式用于将一组复杂的类包装到一个简单的外部接口中。


原来的方式


//依次创建三个部门实例
Paymentpay=newPaymentImpl();
Cookcook=newCookImpl();
Waiterwaiter=newWaiterImpl();
//依次调用三个部门实例的方法来实现用餐功能
Stringfood=pay.pay();
food=cook.cook(food);
waiter.serve(food);

门面模式


publicclassFacade{
//定义被Facade封装的三个部门
Paymentpay;
Cookcook;
Waiterwaiter;//构造器
publicFacade(){
this.pay=newPaymentImpl();
this.cook=newCookImpl();
this.waiter=newWaiterImpl();
}publicvoidserveFood(){
//依次调用三个部门的方法,封装成一个serveFood()方法
Stringfood=pay.pay();
food=cook.cook(food);
waiter.serve(food);
}
}

门面模式调用


Facadef=newFacade();
f.serveFood();


5.策略模式

策略模式用于封装系列的算法,这些算法通常被封装在一个被称为Context的类中,客户端程序可以自由选择其中一种算法,或让Context为客户端选择一种最佳算法——使用策略模式的优势是为了支持算法的自由切换。


publicinterfaceDiscountStrategy
{
//定义一个用于计算打折价的方法
doublegetDiscount(doubleoriginPrice);
}
publicclassOldDiscountimplementsDiscountStrategy{
//重写getDiscount()方法,提供旧书打折算法
publicdoublegetDiscount(doubleoriginPrice){
System.out.println("使用旧书折扣...");
returnoriginPrice*0.7;
}
}
//实现DiscountStrategy接口,实现对VIP打折的算法
publicclassVipDiscountimplementsDiscountStrategy{
//重写getDiscount()方法,提供VIP打折算法
publicdoublegetDiscount(doubleoriginPrice){
System.out.println("使用VIP折扣...");
returnoriginPrice*0.5;
}
}
publicclassDiscountContext
{
//组合一个DiscountStrategy对象
privateDiscountStrategystrategy;
//构造器,传入一个DiscountStrategy对象
publicDiscountContext(DiscountStrategystrategy)
{
this.strategy=strategy;
}
//根据实际所使用的DiscountStrategy对象得到折扣价
publicdoublegetDiscountPrice(doubleprice)
{
//如果strategy为null,系统自动选择OldDiscount类
if(strategy==null)
{
strategy=newOldDiscount();
}
returnthis.strategy.getDiscount(price);
}
//提供切换算法的方法
publicvoidsetDiscount(DiscountStrategystrategy)
{
this.strategy=strategy;
}
}
publicstaticvoidmain(String[]args)
{
//客户端没有选择打折策略类
DiscountContextdc=newDiscountContext(null);
doubleprice1=79;
//使用默认的打折策略
System.out.println("79元的书默认打折后的价格是:"
+dc.getDiscountPrice(price1));
//客户端选择合适的VIP打折策略
dc.setDiscount(newVipDiscount());
doubleprice2=89;
//使用VIP打折得到打折价格
System.out.println("89元的书对VIP用户的价格是:"
+dc.getDiscountPrice(price2));
}


6.观察者模式

观察者模式定义了对象间的一对多依赖关系,让一个或多个观察者对象观察一个主题对象。当主题对象的状态发生变化时,系统能通知所有的依赖于此对象的观察者对象,从而使得观察者对象能够自动更新。


观察者:观察者也是一个接口,该接口规定了具体观察者用来更新数据的方法.


publicinterfaceObserver{
voidupdate(Observableo,Objectarg);
}

主题:主题是一个接口,该接口规定了具体主题需要实现的方法,比如添加、删除观察者以及通知观察者更新数据的方法


importjava.util.ArrayList;
importjava.util.List;
importjava.util.Iterator;publicabstractclassObservable{
//用一个List来保存该对象上所有绑定的事件监听器
Listobservers=newArrayList();//定义一个方法,用于从该主题上注册观察者
publicvoidregistObserver(Observero){
observers.add(o);
}
//定义一个方法,用于从该主题中删除观察者
publicvoidremoveObserver(Observero){
observers.add(o);//通知该主题上注册的所有观察者
publicvoidnotifyObservers(Objectvalue){
//遍历注册到该被观察者上的所有观察者
for(Iteratorit=observers.iterator();it.hasNext();){
Observero=(Observer)it.next();
//显式每个观察者的update方法
o.update(this,value);
}
}
}

具体主题:具体主题是一个实现主题接口的类,该类包含了会经常发生变化的数据。而且还有一个集合,该集合存放的是观察者的引用。


publicclassProductextendsObservable{
//定义两个属性
privateStringname;
privatedoubleprice;//无参数的构造器
publicProduct(){
}publicProduct(Stringname,doubleprice){
this.name=name;
this.price=price;
}publicStringgetName(){
returnname;
}//当程序调用name的setter方法来修改Product的name属性时
//程序自然触发该对象上注册的所有观察者
publicvoidsetName(Stringname){
this.name=name;
notifyObservers(name);
}publicdoublegetPrice(){
returnprice;
}//当程序调用price的setter方法来修改Product的price属性时
//程序自然触发该对象上注册的所有观察者
publicvoidsetPrice(doubleprice){
this.price=price;
notifyObservers(price);
}
}

具体观察者:具体观察者是实现了观察者接口的一个类。具体观察者包含有可以存放具体主题引用的主题接口变量,以便具体观察者让具体主题将自己的引用添加到具体主题的集合中,让自己成为它的观察者,或者让这个具体主题将自己从具体主题的集合中删除,使自己不在时它的观察者.


importjavax.swing.JFrame;
importjavax.swing.JLabel;publicclassNameObserverimplementsObserver{
//实现观察者必须实现的update方法
publicvoidupdate(Observableo,Objectarg){
if(arginstanceofString){
//产品名称改变值在name中
Stringname=(String)arg;
//启动一个JFrame窗口来显示被观察对象的状态改变
JFramef=newJFrame("观察者");
JLabell=newJLabel("名称改变为:"+name);
f.add(l);
f.pack();
f.setVisible(true);
System.out.println("名称观察者:"+o+"物品名称已经改变为:"+name);
}
}
}
publicclassPriceObserverimplementsObserver{
//实现观察者必须实现的update方法
publicvoidupdate(Observableo,Objectarg){
if(arginstanceofDouble){
System.out.println("价格观察者:"+o+"物品价格已经改变为:"+arg);
}
}
}

测试:


publicclassTest{
publicstaticvoidmain(String[]args){
//创建一个被观察者对象
Productp=newProduct("电视机",176);
//创建两个观察者对象
NameObserverno=newNameObserver();
PriceObserverpo=newPriceObserver();
//向被观察对象上注册两个观察者对象
p.registObserver(no);
p.registObserver(po);
//程序调用setter方法来改变Product的name和price属性
p.setName("书桌");
p.setPrice(345f);
}
}


7.代理模式

代理模式是一种应用非常广泛的设计模式,当客户端代码需要调用某个对象时,客户端实际上不关心是否准确得到该对象,它只要一个能提供该功能的对象即可,此时我们就可返回该对象的代理(Proxy)。


代理就是一个Java对象代表另一个Java对象来采取行动。如:


publicclassImageProxyimplementsImage
{
//组合一个image实例,作为被代理的对象
privateImageimage;
//使用抽象实体来初始化代理对象
publicImageProxy(Imageimage)
{
this.image=image;
}
/**
*重写Image接口的show()方法
*该方法用于控制对被代理对象的访问,
*并根据需要负责创建和删除被代理对象
*/
publicvoidshow()
{
//只有当真正需要调用image的show方法时才创建被代理对象
if(image==null)
{
image=newBigImage();
}
image.show();
}
}

如:Hibernate默认启用延迟加载,当系统加载A实体时,A实体关联的B实体并未被加载出来,A实体所关联的B实体全部是代理对象——只有等到A实体真正需要访问B实体时,系统才会去数据库里抓取B实体所对应的记录。



8.命令模式:

某个方法需要完成某一个功能,完成这个功能的大部分步骤已经确定了,但可能有少量具体步骤无法确定,必须等到执行该方法时才可以确定。(在某些编程语言如Ruby、Perl里,允许传入一个代码块作为参数。但Jara暂时还不支持代码块作为参数)。


在Java中,传入该方法的是一个对象,该对象通常是某个接口的匿名实现类的实例,该接口通常被称为命令接口,这种设计方式也被称为命令模式。


publicinterfaceCommand
{
//接口里定义的process方法用于封装“处理行为”
voidprocess(int[]target);
}
publicclassProcessArray
{
//定义一个each()方法,用于处理数组,
publicvoideach(int[]target,Commandcmd)
{
cmd.process(target);
}
}
publicclassTestCommand
{
publicstaticvoidmain(String[]args)
{
ProcessArraypa=newProcessArray();
int[]target={3,-4,6,4};
//第一次处理数组,具体处理行为取决于Command对象
pa.each(target,newCommand()
{
//重写process()方法,决定具体的处理行为
publicvoidprocess(int[]target)
{
for(inttmp:target)
{
System.out.println("迭代输出目标数组的元素:"+tmp);
}
}
});
System.out.println("------------------");
//第二次处理数组,具体处理行为取决于Command对象
pa.each(target,newCommand()
{
//重写process方法,决定具体的处理行为
publicvoidprocess(int[]target)
{
intsum=0;
for(inttmp:target)
{
sum+=tmp;
}
System.out.println("数组元素的总和是:"+sum);
}
});
}
}


9.桥接模式

由于实际的需要,某个类具有两个以上的维度变化,如果只是使用继承将无法实现这种需要,或者使得设计变得相当臃肿。而桥接模式的做法是把变化部分抽象出来,使变化部分与主类分离开来,从而将多个的变化彻底分离。最后提供一个管理类来组合不同维度上的变化,通过这种组合来满足业务的需要。


Peppery口味风格接口:


publicinterfacePeppery
{
Stringstyle();
}

口味之一


publicclassPepperySytleimplementsPeppery
{
//实现"辣味"风格的方法
publicStringstyle()
{
return"辣味很重,很过瘾...";
}
}


口味之二


publicclassPlainStyleimplementsPeppery
{
//实现"不辣"风格的方法
publicStringstyle()
{
return"味道清淡,很养胃...";
}
}

口味的桥梁


publicabstractclassAbstractNoodle
{
//组合一个Peppery变量,用于将该维度的变化独立出来
protectedPepperystyle;
//每份Noodle必须组合一个Peppery对象
publicAbstractNoodle(Pepperystyle)
{
this.style=style;
}
publicabstractvoideat();
}

材料之一,继承口味


publicclassPorkyNoodleextendsAbstractNoodle
{
publicPorkyNoodle(Pepperystyle)
{
super(style);
}
//实现eat()抽象方法
publicvoideat()
{
System.out.println("这是一碗稍嫌油腻的猪肉面条。"
+super.style.style());
}
}

材料之二,继承口味


publicclassBeefMoodleextendsAbstractNoodle
{
publicBeefMoodle(Pepperystyle)
{
super(style);
}
//实现eat()抽象方法
publicvoideat()
{
System.out.println("这是一碗美味的牛肉面条。"
+super.style.style());
}
}

主程序


publicclassTest
{
publicstaticvoidmain(String[]args)
{
//下面将得到“辣味”的牛肉面
AbstractNoodlenoodle1=newBeefMoodle(
newPepperySytle());
noodle1.eat();
//下面将得到“不辣”的牛肉面
AbstractNoodlenoodle2=newBeefMoodle(
newPlainStyle());
noodle2.eat();
//下面将得到“辣味”的猪肉面
AbstractNoodlenoodle3=newPorkyNoodle(
newPepperySytle());
noodle3.eat();
//下面将得到“不辣”的猪肉面
AbstractNoodlenoodle4=newPorkyNoodle(
newPlainStyle());
noodle4.eat();
}
}



最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台