设计模式主要可以分为三大类:创建型模式、结构型模式、行为型模式创建型模式。
创建型模式旨在对象的创建,如工厂模式,单例模式等;结构型模式关注类和对象的组合,如适配器模式,装饰器模式等;行为模式更关注对象的的行为与对象之间的通信,如策略模式,状态模式等,以下是我对这些设计模式的简单理解。
1.工厂模式
工厂模式属于创建型模式,有简单工厂、工厂方法模式和抽象工厂模式。
简单工厂和工厂方法模式主要是用来创建某一类产品,抽象工厂模式则是用来创建某一个产品族的产品。
简单工厂模式的做法是创建一个工厂类,根据客户端提供的不同的参数返回不同的对象,严格的来说简单工厂不能算是一种设计模式。工厂方法模式则是简单工厂模式的升级版,将对象的创建方法抽象到接口中,然后将对象的具体创建工作延迟到工厂实现子类,并通过不同的工厂实现类创建不同的对象。
抽象工厂模式通常用在系统更多的是消费某一产品族的产品的时候。抽象工厂模式会有一个抽象工厂接口来规定我要创建哪些抽象类型的产品,所有创建的产品需要实现相同的接口,然后具体工厂类需要实现这个抽象工厂的接口来创建自己的产品族。如我们可以创建一个服装的抽象工厂定义创建裤子和创建鞋子两个方法。下面我们可以创建一个男装工厂来创建男装产品族和产品族并创建一个女装工厂来创建女装产品族。
public interface ClothFactory {
Cloth getPaints();
Cloth getShoes();
}
public class ManClothFactory implements ClothFactory {
@Override
public Cloth getPaints() {
return new ManPaints();
}
@Override
public Cloth getShoes() {
return new ManShoes();
}
}
......
这样当系统来一个男人,我们只需要从男装工厂拿鞋子和裤子即可。而这时若使用工厂方法可能就要从鞋子工厂拿一双男鞋,从裤子工厂拿一条男裤子。
2. 单例模式
单例模式是一种创建型模式,目的是使得某个类只有一个对象实例,核心代码是将构造函数私有化,并通过静态方法的方式返回实例。这里只列举一种高效且线程安全的单例模式的实现。
public class SingleDemo {
private volatile static SingleDemo singleDemo;
private SingleDemo() {
}
public static SingleDemo getInstance() {
if (singleDemo == null) {
synchronized (SingleDemo.class) {
if (singleDemo == null) {
singleDemo = new SingleDemo();
}
}
}
return singleDemo;
}
只有当对象为空时才进行对象的创建,否则直接返回。创建实例时为了保证线程安全,首先获得该单例类的类锁,volatile关键字则保证了该单例对象在第二层非空判断中的可见性。
3. 观察者模式
观察者模式是一种行为型模式,该模式可以使得一个对象的状态发生改变时,可以使得关注该对象的对象都发生相应改变。
为实现观察者模式,需要实现一个Subject(主题)类,并在主题类中维护一个观察者列表,所有的观察者必须实现一个抽象的观察者接口,并实现其中的update的方法,而且每个观察者有会持有一个他所关注主题对象的引用,用来在主题类的观察者列表中注册或者注销。其次,主题里还要提供观察者的注册和注销的方法,还要有通知所有的观察者的方法 notifyAll(即遍历观察者列表,执行其中的update方法,为了提高可用性,一般采用异步的方式)。主题对象状态发生改变时,只需要调用notifyAll方法,即可通知所有的观察者。
4.模板方法模式
模板方法模式是一种行为型模式,定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤,通常用来工共代码的提取。比如你去上班,首先你要出家门,最后进公司。中间你可能坐地铁,也可能打车等等。需要提供一个接口和一个实现了该接口的抽象类,把需要个性化定制的部分做成抽象方法让子类去实现,并将模板方法定义成final不允许被重写。这样客户端只需关注自己需要实现的部分即可。
5.备忘录模式
备忘录模式是一种行为型模式。在不改变原有对象的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,并且可以恢复之前的状态。比如游戏中的存档,你可以随时保存你当前的游戏状态,并在下次游戏时恢复到该状态。做法是创建一个和原对象相同结构的副本对象Memento,并在原对象中创建createMemento(创建副本)和setFromMemeto(恢复成该副本)两个方法,同时,我们需要创建一个CareTaker(备忘录)类来记录和保存该副本对象,备份原对象时,把生成的Memento对象存到备忘录中,恢复的时候从备忘录拿到副本执行恢复方法即可。
6.策略模式
策略模式是一种行为型模式,它定义了一系列的算法,并将每一个算法封装起来,而且使他们可以相互替换,让算法独立于使用它的客户而独立变化。
策略模式以一系列接口的形式定义算法的行为,并将算法的具体的实现交给子类,处理同一件事的算法实现同一接口。而客户端使用他们时只需要以接口的形式进行声明和使用,只需要在构造函数为算法指定具体的实现类即可。这样我们就可以初始化不同的算法实现类使得客户端具备不同的行为,而且还能动态修改算法的实现类来修改客户端的行为。
7.迭代器模式
迭代器模式是一种行为型模式。以一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。通常用来进行容器元素的迭代,这样容器内部就只需维护自己的数据结构,而将元素的迭代操作交给迭代器做。因为迭代行为要访问容器的内部元素,所以通常以内部类的方式声明容器的迭代器,在JAVA中可以实现Iterator接口,重写hasNext() 和next()方法定义自己的迭代行为(如正向迭代,反向迭代等),并在容器类中声明一个方法来返回实例化后的迭代器对象 。这样,客户端就可以可以选择使用不同的容器迭代器去访问该容器的元素了。
8.外观模式
外观模式是一种结构型模式。它提供一个更高层的接口,使得整个系统和子系统更容易使用,比如我们要开电脑时可能要启动CPU、硬盘,内存等,而Computer类只对外暴漏一个开机方法,他持有CPU,硬盘,内存等对象的引用 ,在并在开机方法中调用CPU、硬盘的启动方法。这样便降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。
9.建造者模式
建造者模式是一种创建型模式。他主要用来在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。用户只需告诉系统需要哪个产品即可。比如说你要建房子,你可以选择自己设计,自己盖,也可以请一个工程队,自己指挥他们盖。当然更多是请一个更有经验的人来指挥工程队干,而这就类似建造者模式,以下是代码实现。HouseBuider(抽象的建造者)接口定义了抽象的建造接口,比如我们要建一个平房,需要实现一个平房的建造者,而且可以通过实现不同的建造者来建造不同的房子,客户端在使用时,只需要调用指挥者的建房子方法即可,他接受一个工程队(具体的建造者)并指挥他们干活,客户端可以通过建造者的getHouse方法拿到建好的房子。
public class House { private String floor; private String wall; private String HouseTop; public String getFloor() { return floor; } @Override public String toString() { return "House{" + "floor='" + floor + '\'' + ", wall='" + wall + '\'' + ", HouseTop='" + HouseTop + '\'' + '}'; } public void setFloor(String floor) { this.floor = floor; } public String getWall() { return wall; } public void setWall(String wall) { this.wall = wall; } public String getHouseTop() { return HouseTop; } public void setHouseTop(String houseTop) { HouseTop = houseTop; } }
public interface HouseBuilder { //建地板 void buildFloor(); //建围墙 void buildWall(); //建房顶 void buildHouseTop(); House getHouse(); }
//平房工程队 public class PingfangBuilder implements HouseBuilder { private House house; public PingfangBuilder() { house = new House(); } @Override public void buildFloor() { house.setFloor("平方地板"); } @Override public void buildWall() { house.setWall("平方墙"); } @Override public void buildHouseTop() { house.setHouseTop("平方房顶"); } @Override public House getHouse() { return house; } }
//具体的指挥者 public class Director { public void buildHouse(HouseBuilder builder) { builder.buildFloor(); builder.buildHouseTop(); builder.buildWall(); } }
10.适配器模式
适配器模式是一种结构型模式,将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。可以通过继承和依赖(推荐)的方式实现适配。比如系统只接受狗,但我这里只有一只猫,我只需创建一个猫的适配器类去实现狗实现的接口,并持有猫这个对象的引用。比如这个接口中只有一个叫的方法,只需要在适配器的实现方法里调用猫的叫方法。这样就完成了猫到狗的适配,可能不太形象,大家可以想个更形象的例子。。。
11.享元模式
享元模式属于结构型模式,主要用于减少创建对象的数量,以减少内存占用和提高性能。其核心代码是使用HashMap来存储这些对象,提供一个工厂类来维护这个HashMap,并提供对象的访问方法,如果该key值有对应的对象,直接返回,否则创建并存到HashMap中,最后返回该对象。
12.原型模式
原型模式是一种创建型模式,是用于创建重复的对象,同时又能保证性能。在java中,只需要实现Cloneable接口,并重写里面的clone方法。
13.访问者模式
访问者模式是一种行为型模式,这是一种较为复杂的设计模式。这种设计模式可以使得系统可以对于不同的访问者做出不同的接待行为,他旨在将系统的稳定的数据结构和易变的行为进行解耦。比如说一个公园,他有A部分和B部分,他的构造较为稳定 ,而公园每天会接纳不同的人,而接纳行为就属于易变的部分。这里定义了一个公园组件的接口,他负责接纳抽象的访问者(即调用Vistor的visit方法),A部分和B部分都需要实现这个接口 。
public interface ParkElements { void accept(Visitor visitor); }
public class Park { private String name; private ParkA parkA; private ParkB parkB; public Park(){ parkA=new ParkA(); parkB=new ParkB(); } public void accept(Visitor visitor) { visitor.vistit(this); visitor.vistit(parkA); visitor.vistit(parkB); } }
public class ParkA implements ParkElements { @Override public void accept(Visitor visitor) { visitor.vistit(this); } }
public class ParkB implements ParkElements { @Override public void accept(Visitor visitor) { visitor.vistit(this); } }
下面是访问者访问公园不同部分时的接口。
public interface Visitor { void vistit(Park park); void vistit(ParkA park); void vistit(ParkB park); }
比如说VistorA只允许访问ParkA,就只实现第二个重载方法即可。
public class VisitorA implements Visitor { @Override public void vistit(Park park) { } @Override public void vistit(ParkA park) { System.out.println("visitA 访问 parkA" ); } @Override public void vistit(ParkB park) { } }
在客户端中只需这样调用
public class MainClass { public static void main(String[] args) { Visitor visitor = new VisitorA(); Park park = new Park(); park.accept(visitor); } }
accept的方法会接受一个访问者,由于VistorA只对访问parkA的方法进行了实现,所以程序输出:
visitA 访问 parkA
14.命令模式
命令模式是一种行为型模式。将一个请求(命令)封装成一个对象,从而使得可以用不同的请求对客户进行参数化。
主要是用来将行为的发起者,和行为的实现者进行解耦,并支持回滚。重做,撤销等操作
下面举一个例子,必如我们要提高电视机的音量,我们需要操作遥控器(命令的发起者或者说是入口)点击音量+按钮(具体的命令)提高电视机(命令的接收实现者)的音量。遥控器对象维护自己的Commad命令对象的存储,并支持set操作,点击按钮是其实是执行的命令对象的执行方法,而命令对象持有命定接受者TV的引用,会调用TV的增加音量的方法。
//命令的持有者--命令执e行的入口 public class YaoKongQi { Comand comand; private Map<Integer, Comand> map = new HashMap<Integer, Comand>(); public Comand getComand(Integer index) { return map.get(index); } public void setComand(Integer index, Comand comand) { map.put(index,comand); } public void excute(Integer index) { map.get(index).excute(); } }
//命令接口 public abstract class Comand { protected TV tv; public Comand(TV tv) { this.tv = tv; } abstract void excute(); }
//具体的命令,增加音量 public class SoundUpCommand extends Comand { public SoundUpCommand(TV tv) { super(tv); } @Override void excute() { tv.soundUp(); } }
//命令的接收者Reciver public class TV { public void soundUp(){ System.out.println("提高音量"); } public void soundDown(){ System.out.println("降低音量"); } }
这样就完成了使用的遥控器对电视机音量的控制。
15.中介者模式
中介者模式属于行为型模式。旨在降低多个对象和类之间通信的复杂性,采用一个中介对象封装一系列对象的交互.,比如我们比较两个对象的匹配程度,需要两者都持有对方的引用,而借助中介者,中介持有两个对象的引用,便可解决这一问题.以下是一个对象匹配度小程序.下面定义了一个抽象的Person接口,需要子类去实现一个getParterner方法,其实就是把自己设置到中介者的引用里并执行中介者的比较方法.
public abstract class Person { //姓名 private String name; //评分 private int condition; //中介者 private Mediator mediator; public Person(String name, int condition, Mediator mediator) { this.name = name; this.condition = condition; this.mediator = mediator; } abstract void getParterner(Person persion); public Mediator getMediator() { return mediator; } public void setMediator(Mediator mediator) { this.mediator = mediator; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCondition() { return condition; } public void setCondition(int condition) { this.condition = condition; } }
public class Women extends Person { public Women(String name, int condition, Mediator mediator) { super(name, condition, mediator); } @Override void getParterner(Person persion) { this.getMediator().setWomen(this); this.getMediator().getParterner(persion); } }
public class Man extends Person { public Man(String name, int condition, Mediator mediator) { super(name, condition, mediator); } @Override void getParterner(Person persion) { this.getMediator().setMan(this); this.getMediator().getParterner(persion); } }
//中介者 public class Mediator { private Man man; private Women women; public Man getMan() { return man; } public void setMan(Man man) { this.man = man; } public Women getWomen() { return women; } public void setWomen(Women women) { this.women = women; } public void getParterner(Person person) { if (person instanceof Women) { this.setWomen((Women) person); } if (person instanceof Man) { this.setMan((Man) person); } if (man == null || women == null) { System.out.println("我不是同性恋"); } else { if (this.getMan().getCondition() == this.getWomen().getCondition()) { System.out.println("绝配"); } else { System.out.println("不合适"); } } } }
public class MainClass { public static void main(String[] args) { Mediator mediator=new Mediator(); Person person1=new Man("zhangsan",4,mediator); Person person2=new Man("lisi",4,mediator); Person person3=new Women("xiaomei",4,mediator); person1.getParterner(person2); person1.getParterner(person3); } }
16.装饰器模式
装饰器模式是一种结构型模式,允许向一个现有的对象添加新的功能,同时又不改变其结构。它是作为现有的类的一个包装。
实现:装饰器对象和被装饰的对象需要实现相同的接口,这主要是为了保证对象被装饰后类型的统一,并无其他作用.装饰器类持有被装饰对象的引用并在构造函数中赋值,可以对被装饰对象的方法进行增强或者其他操作,可以选择使用不同的装饰器具备不同的行为。JAVA的IO系统就是一个比较完善的装饰器模式的实现。
17.责任链模式
责任链模式是一种行为型模式,为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为型模式。通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。如JAVA WEB应用中的过滤器就是使用了这种模式.下面通过创建汽车的例子实现这种设计模式,
//抽象的处理类 public abstract class Build { protected Build next; abstract void build(); public Build setNext(Build build) { this.next = build; return build; } }
public class TopBuild extends Build { @Override public void build() { System.out.println("创建车头"); if (next != null) next.build(); } }
public class MidBuild extends Build { @Override public void build() { System.out.println("建造车身"); if (next != null) next.build(); } }
public class MidumBuild extends Build { @Override public void build() { System.out.println("创建车尾"); if (next != null) next.build(); } }
public class MainClass { public static void main(String[] args) { Build build1 = new TopBuild(); Build build2 = new MidBuild(); Build build3 = new MidumBuild(); build1.setNext(build2).setNext(build3); build1.build(); } }
17.代理模式
代理模式是一种结构型模式,使得用户可以不直接和目标对象打交道,而是通过代理对象和目标对象打交道的方式操作原始对象。被代理的对象和代理对象需要实现相同的接口,保证类型的统一,代理对象持有被代理对象的引用,可以对被代理的对象进行方法的增强等操作。在JAVA中主要有静态代理(需要自己实现代理类),JDK动态代理(不需要实现代理类,需要实现一个InvocationHandler(调用处理器) 处理代理逻辑,通过反射的方式调用目标方法),Cglib代理。
18.状态模式
状态模式是一种行为型模式,旨在使得对象在不同的状态的具备不同的行为,我的理解是使得对象在不同的状态下对于相同的操作做出不同的响应,并自动维护当前的状态。状态会有一个State接口规范会有状态的持有者会有哪些动作,状态的持有者和状态对象都需要实现这个接口,状态的持有者需要维护当前的状态,并持有所有状态的引用,执行动作方法其实就是执行当前状态下的动作方法,状态对象也需要持有状态所有者的引用,用来修改状态持有者当前的状态。状态持有者对动作的相应其实就是调用当前状态对象处理的相同动作的执行方法,拿电梯(状态持有者)运行的例子来说,当电梯无人使用时你点击开门按钮,门会打开,并将电梯从关闭状态变为开门状态,而你在电梯的运行状态点击开门电梯并不会响应你,以下是代码实现,只是一些简单的操作,真实环境应该比这复杂的多。
//电梯用户的操作 public interface State { //开门 public void open(); //关门 public void close(); //运行 public void yunxing(); }
//状态的持有者 public class DianTi implements State { State openState; State closeState; State runningState; public State getOpenState() { return openState; } public State getCloseState() { return closeState; } public State getRunningState() { return runningState; } State currentState; public State getCurrentState() { return currentState; } public void setCurrentState(State currentState) { this.currentState = currentState; } public DianTi() { openState = new OpenState(this); closeState = new CloseState(this); runningState = new RunningState(this); currentState = closeState; } @Override public void open() { this.getCurrentState().open(); } @Override public void close() { this.getCurrentState().close(); } @Override public void yunxing() { this.getCurrentState().yunxing(); } }
public class CloseState implements State { private DianTi dianTi; public CloseState(DianTi dianTi) { this.dianTi = dianTi; } @Override public void open() { System.out.println("门已经打开了"); this.dianTi.setCurrentState(dianTi.getOpenState()); } @Override public void close() { System.out.println("do nothing!"); } @Override public void yunxing() { System.out.println("电梯已运行"); this.dianTi.setCurrentState(dianTi.getRunningState()); }
public class OpenState implements State { private DianTi dianTi; public OpenState(DianTi dianTi) { this.dianTi = dianTi; } @Override public void open() { System.out.println("do nothing !"); } @Override public void close() { System.out.println("已关闭"); dianTi.setCurrentState(dianTi.getCloseState()); } @Override public void yunxing() { System.out.println("请先关闭电梯门"); } }
public class RunningState implements State { private DianTi dianTi; public RunningState(DianTi dianTi) { this.dianTi = dianTi; } @Override public void open() { System.out.println("电梯运行中,不能开门"); } @Override public void close() { System.out.println("do nothing !"); } @Override public void yunxing() { System.out.println("do nothing"); } }
public class MainClass { public static void main(String[] args) { DianTi dianTi=new DianTi(); //关闭状态 dianTi.close(); dianTi.open(); System.out.println(); dianTi.yunxing(); dianTi.close(); dianTi.yunxing(); System.out.println(); dianTi.open(); // } }