注意,一些设计模式之间的(代码)"形式"虽然相同或相似,但是"语义"是截然不同的。个人认为设计模式提倡的是一种编码思想,而不是规范。
为什么要使用设计模式?
为了代码复用,增加可维护性
|
设计模式的六大原则1、开闭原则(Open Close Principle) 开闭原则的意思是:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。 2、里氏代换原则(Liskov Substitution Principle) 里氏代换原则是面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。LSP 是继承复用的基石,只有当派生类可以替换掉基类,且软件单位的功能不受到影响时,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。里氏代换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。 3、依赖倒转原则(Dependence Inversion Principle) 这个原则是开闭原则的基础,具体内容:针对对接口编程,依赖于抽象而不依赖于具体。 4、接口隔离原则(Interface Segregation Principle) 这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。它还有另外一个意思是:降低类之间的耦合度。由此可见,其实设计模式就是从大型软件架构出发、便于升级和维护的软件设计思想,它强调降低依赖,降低耦合。 5、迪米特法则,又称最少知道原则(Demeter Principle) 最少知道原则是指:一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。 6、合成复用原则(Composite Reuse Principle) 合成复用原则是指:尽量使用合成/聚合的方式,而不是使用继承。 |
创建型模式(共五种)
工厂方法模式:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 |
个人总结: 通过对象工厂灵活地生产多种对象 |
public class Factory_Method { public static void main(String[] args){ AnimalsFactory animalsFactory = new AnimalsFactory(); //通过工厂创建一个cat对象 Animals cat = animalsFactory.getAnimals("cat"); //通过工厂创建一个dog对象 Animals dog = animalsFactory.getAnimals("dog"); cat.name(); dog.name(); } } interface Animals { //动物 void name(); } class Cat implements Animals{ //猫 @Override public void name(){ System.out.println("this is a cat"); } } class Dog implements Animals{ //狗 @Override public void name(){ System.out.println("this is a dog"); } } class AnimalsFactory { //动物工厂 public Animals getAnimals(String name){ if(name.equalsIgnoreCase("cat")) return new Cat(); else if(name.equalsIgnoreCase("dog")) return new Dog(); else return null; } }
|
抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 |
个人总结:工厂模式 >> 一种工厂,多种对象 ; 抽象工厂模式 >> 一种抽象工厂,多种工厂,每个工厂又可以生产多种对象 |
public class Abstract_Factory { //工厂生成器:生产具体的工厂 public static AbstractFactory getFactory(String factoryName){ if(factoryName.equalsIgnoreCase("animals")) return new AnimalsFactory(); else if(factoryName.equalsIgnoreCase("others")) return new OthersFactory(); else return null; } public static void main(String[] args){ //生产动物工厂 AbstractFactory animalsFactory = getFactory("animals"); //通过动物工厂创建一个cat对象 Animals cat = animalsFactory.getAnimals("cat"); cat.name(); } } interface Animals { //动物 void name(); } class Cat implements Animals{ //猫 @Override public void name(){ System.out.println("this is a cat"); } } class AnimalsFactory extends AbstractFactory{ //动物工厂 public Animals getAnimals(String name){ if(name.equalsIgnoreCase("cat")) return new Cat(); else return null; } @Override public Object getObject() { return null; } } class OthersFactory extends AbstractFactory{ //其他工厂 public Object getObject() { return null; } @Override public Animals getAnimals(String name) { return null; } } abstract class AbstractFactory { //抽象工厂 abstract public Animals getAnimals(String name); abstract public Object getObject(); }
|
单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
|
个人总结:在类内部创造单一对象,通过设置构造方法权限,使类外部无法再创造对象
|
public class Singleton { public static void main(String[] agrs){ Earth earth = Earth.getEarth(); System.out.println(earth.getAge()); } } //创建单例对象的方式有多种,下面是比较常用的一种方式;按需求选择合适方式。 class Earth { //只允许创建一个对象的类 //创建唯一对象 private static Earth earth = new Earth(); //构造函数访问权限必须private private Earth(){} //获取唯一对象 public static Earth getEarth(){ return earth; } private int age = 1000; public int getAge() { return age; } }
|
建造者模式:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
|
个人总结:将一些不会变的基本组件,通过builder,组合,构建复杂对象,实现分离
|
public class BuilderDemo { public static void main(String[] args) { PriceBuilder priceBuilder = new PriceBuilder(); System.out.println("Car1和Car2:"+priceBuilder.Car1AndCar2()); System.out.println("Car1和Bus:"+priceBuilder.Car1AndBus()); } } //基本组件 interface Car { } //基本组件1 class Car1 implements Car{ int price = 20; } //基本组件2 class Car2 implements Car{ int price = 90; } //基本组件3 class Bus { int price = 500; } class PriceBuilder { //car1和car2的总价格 public int Car1AndCar2() { int priceOfCar1 = new Car1().price; int priceOfCar2 = new Car2().price; return priceOfCar1+priceOfCar2; } //car1和bus的总价格 public int Car1AndBus() { int priceOfCar1 = new Car1().price; int priceOfBus = new Bus().price; return priceOfCar1+priceOfBus; } }
|
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
|
个人总结:
将对象复制了一份并返还给调用者,对象需继承Cloneable并重写clone()方法
|
public class Prototype implements Cloneable{ private String message = "hello"; public Object clone() throws CloneNotSupportedException{ Prototype proto = (Prototype) super.clone(); //操作克隆对象 proto.message += " world!"; return proto; } public static void main(String[] args) throws CloneNotSupportedException { Prototype p = (Prototype)new Prototype().clone(); //操作克隆对象 System.out.println(p.message); } }
|
结构型模式(共七种)
适配器模式:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
|
个人总结:衔接两个不兼容、独立的接口的功能,使得它们能够一起工作。适配器起中介作用。 |
public class Adapter { public static void main(String[] args) { //兼容了高级功能的普通播放器 Player player = new Player(); player.play(); } } //普通的播放器 interface MediaPlayer { public void play(); } //高级的播放器 interface AdvanceMediaPlayer { public void playVideo(); } //视频播放器(高级的播放器) class VideoPlayer implements AdvanceMediaPlayer { @Override public void playVideo(){ System.out.println("play video!"); } } //适配器(衔接了普通播放器与高级播放器这两个独立接口的功能) class MediaAdapter implements MediaPlayer { AdvanceMediaPlayer advanceMediaPlayer; public MediaAdapter() { advanceMediaPlayer = new VideoPlayer(); } @Override public void play() { advanceMediaPlayer.playVideo(); } } //普通播放器 class Player implements MediaPlayer { //兼容高级播放器的适配器 MediaAdapter mediaAdapter = new MediaAdapter(); @Override public void play() { mediaAdapter.play(); } }
|
装饰器模式:动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。
|
个人总结:创建类的装饰类,对被装饰类增强功能。装饰模式是继承的一个替代模式。
|
public class Decorator { public static void main(String[] args) { Animals dog = new AnimalsDecorator(new Dog()); dog.run(); } } interface Animals { public void run(); } //被装饰类 class Dog implements Animals{ @Override public void run() { System.out.println("dog run!"); } } //装饰类 class AnimalsDecorator implements Animals { private Animals animals; //动态装饰,参数为Animals接口,传入什么实现就装饰什么实现 //继承不能做到这一点,继承的功能是静态的,不能动态增删。 public AnimalsDecorator(Animals animals) { this.animals = animals; } @Override //装饰run()方法 public void run() { animals.run(); System.out.println("fast!"); } }
|
代理模式:为其他对象提供一种代理以控制对这个对象的访问。
|
个人总结:创建类的代理类,间接访问被代理类的过程中对其功能加以控制,例如在某个函数执行前后添加额外功能。(代理例子:买火车票不一定在火车站买,也可以去代售点)。
和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制,"形式"虽然相似,"语义"却截然不同"。起中介作用
|
public class Proxy { public static void main(String[] args) { Animals dog = new DogProxy(new Dog()); dog.run(); } } interface Animals { public void run(); } class Dog implements Animals { @Override public void run() { System.out.println("run!"); } } //通过代理类,在被代理类的run()方法执行前后添加额外的功能 class DogProxy implements Animals { private Animals animals; public DogProxy(Animals animals){ super(); this.animals = animals; } @Override public void run() { before(); animals.run(); atfer(); } private void atfer() { System.out.println("after run!"); } private void before() { System.out.println("before run!"); } }
|
外观模式:为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
|
个人总结:在客户端和复杂系统之间再加一层,在这一层中将调用顺序、依赖关系等处理好。提供一个容易使用的外观层。
|
public class Facade { public static void main(String[] args) { Computer computer = new Computer(); computer.put(); } } class CPU { public void work(){ //复杂的操作 System.out.println("CPU is working!"); } } class Disk { public void put(){ //复杂的操作 System.out.println("put in disk!"); } } //外观类,隐藏了系统的复杂性,提供简化的方法(访问系统的接口) //客户端不需要知道系统内部的复杂联系 class Computer { private CPU cpu; private Disk disk; public Computer(){ cpu = new CPU(); disk = new Disk(); } public void work(){ cpu.work(); } public void put(){ disk.put(); } }
|
桥接模式:将抽象部分与实现部分分离,使它们都可以独立的变化。
|
个人总结:通过对Bridge类的调用,实现了对同一接口下不同实现类的调用;建立一个继承于同一抽象的不同实现类之间的关联关系,这个关系由Bridge类桥接起来。
|
public class Bridge { public static void main(String[] args) { AnimalsBridge bridge = new AnimalsBridge(new Dog()); bridge.method(); } } //接口 interface Animals { public void method(); } //实现1 class Cat implements Animals { @Override public void method() { System.out.println("this is cat!"); } } //实现2 class Dog implements Animals { @Override public void method() { System.out.println("this is dog!"); } } //将Animals接口下的不同实现, //通过桥接模式使它们在抽象层建立一个关联关系。 //实现之间独立变化,减少耦合 class AnimalsBridge { private Animals animals; public AnimalsBridge(Animals animals) { this.animals = animals; } public void method(){ animals.method(); } }
|
组合模式:将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
|
个人总结:创建了一个包含自己对象组的类,并提供修改对象组的方法。应用场景,如树形菜单,文件、文件夹的管理。
|
public class Composite { public static void main(String[] args) { Person person = new Person("小明"); person.addFriends(new Person("小红")); person.addFriends(new Person("小白")); System.out.println(person.getFriends()); } } class Person { private String name; //包含自己的对象组 private List
|
享元模式:运用共享技术有效地支持大量细粒度的对象。
|
个人总结:重用现有的同类对象,若未找到匹配的对象,则创建新对象。例如,数据库的连接池。减少对象的创建,降低系统内存,提高效率。 |
public class Flyweight { public static void main(String[] args) { //red Circle默认存在,所以拿的时候不用new Circle circle = CircleFactory.getCircle("red"); circle.draw(); for(int i=0;i<2;i++) { //第一次拿的时候需要new green Circle,第二次拿的时候不用new circle = CircleFactory.getCircle("green"); circle.draw(); } } } class Circle { private String color; public Circle(String color){ this.color = color; } public void draw(){ System.out.println(color+" Circle!"); } } class CircleFactory { private static final HashMap
|
行为型模式(共十一种)
策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
|
个人总结:统一接口下的一系列算法类(多种策略),用一个类将其封装起来,使它们(多种策略)可动态切换。
和工厂模式的区别:工厂模式是创建型模式,是为了创建不同对象;而策略模式是行为模式,为了选择不同的行为。
|
public class Strategy { public static void main(String[] args) { OperationStrategy operationStrategy = new OperationStrategy(new OperationAdd()); operationStrategy.executeStrategy(15, 21); } } interface Operation{ public void doOperation(int a, int b); } //策略1 class OperationAdd implements Operation{ public void doOperation(int a, int b){ System.out.println(a+"+"+b+"="+(a+b)); } } //策略2 class OperationMultiply implements Operation{ public void doOperation(int a, int b){ System.out.println(a+"*"+b+"="+(a*b)); } } //封装一系列策略,可任意替换策略(实现同一个接口) class OperationStrategy{ private Operation operation; public OperationStrategy(Operation operation){ this.operation = operation; } //执行策略 public void executeStrategy(int a, int b){ operation.doOperation(a, b); } }
|
模板方法模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
|
个人总结:将一些固定步骤、固定逻辑的方法封装成模板方法。调用模板方法即可完成那些特定的步骤。例如,spring中对Hibernate的事务管理,开启session、关闭session等固定步骤不需重复写,直接丢给一个实体保存。 |
public class Template { public static void main(String[] args) { Game game = new FootballGame(); game.play(); } } abstract class Game { //步骤1,初始化游戏 abstract void initialize(); //步骤2,开始游戏 abstract void startPlay(); //,步骤3,结束游戏 abstract void endPlay(); //主方法,模板方法,设置为final,在抽象类中实现 public final void play() { initialize(); startPlay(); endPlay(); } } class FootballGame extends Game { @Override void initialize() { System.out.println("Football Game Initialized! Start playing."); } @Override void startPlay() { System.out.println("Football Game Started. Enjoy the game!"); } @Override void endPlay() { System.out.println("Football Game Finished!"); } }
|
观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
|
个人总结:一个对象(被观察者)状态变化时,通知所有依赖于它的对象(观察者);这种依赖方式具有双向性:观察者指定被观察的对象,或者被观察对象添加观察者,下面例子采用后者方式
|
public class Observer { public static void main(String[] args) { Subject subject = new Subject(); subject.addSubjectObserver(new Observer1()); subject.addSubjectObserver(new Observer2()); subject.setState(1); } } class Subject { //一对多关系,多个该类的观察者 private List
|
迭代器模式:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
|
个人总结:Java中的iterator的简单实现原理。将聚合类中遍历各个元素的行为分离出来,封装成迭代器,让迭代器来处理遍历的任务;使简化聚合类,同时又不暴露聚合类的内部。
|
public class IteratorDemo { public static void main(String[] args) { MyContainer myContainer = new MyContainer(); Iterator iterator = myContainer.getIterator(); while(iterator.hashNext()) System.out.println(iterator.next()); } } //迭代器接口 interface Iterator { public boolean hashNext(); public Object next(); } //容器接口 interface Container { public Iterator getIterator(); } //自定义容器(聚合类) class MyContainer implements Container{ public String names[] = {"Robert" , "John" ,"Julie" , "Lora"}; @Override public Iterator getIterator(){ return new MyIterator(); } //自定义迭代器,迭代器类定义为容器类的内部类 private class MyIterator implements Iterator{ int index = 0; //自定义遍历规则 @Override public boolean hashNext(){ if(index < names.length) return true; return false; } @Override public Object next(){ if(this.hashNext()) return names[index++]; return null; } } }
|
责任链模式:避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
|
个人总结:在Handler类里面聚合自己,形成一条Handler链(或树、环等),并且可以将请求往下一个Handler传递(只允许传给另一个,而不允许传给多个)。例子:Struts拦截器,Filter过滤器
|
public class Chain_of_Responsibility { public static void main(String[] args) { ResponsibilityHandler handler1 = new ResponsibilityHandler("handler1"); ResponsibilityHandler handler2 = new ResponsibilityHandler("handler2"); ResponsibilityHandler handler3 = new ResponsibilityHandler("handler3"); handler1.setResponsibilityHandler(handler2); handler2.setResponsibilityHandler(handler3); handler1.operator();//操作请求会沿着这条链传递下去, } } //责任处理器/接收器 class ResponsibilityHandler { //聚合自己,构成一条责任链 private ResponsibilityHandler responsibilityHandler = null; private String name; public ResponsibilityHandler(String name){ this.name = name; } public ResponsibilityHandler next(){ return this.responsibilityHandler; } public void setResponsibilityHandler(ResponsibilityHandler responsibilityHandler){ this.responsibilityHandler = responsibilityHandler; } public void operator(){ System.out.println(name+" is handler!"); if(this.next() != null){ //将请求发送到下一个责任接收器 next().operator(); } } }
|
命令模式:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
|
个人总结:三种角色(调用者→接受者→命令);解耦行为请求者和行为实现着,实现请求和执行分开;调用者选择命令发布,命令指定执行者。
|
public class CommandDemo { public static void main(String[] args) { Receiver receiver = new Receiver("小明"); //指定命令的执行者 Command shootCommand = new ShootCommand(receiver); Command otherCOmmand = new OtherCommand(receiver); Invoker invoker = new Invoker(); invoker.addCommands(shootCommand); invoker.addCommands(otherCOmmand); invoker.sendCommands(); } } //命令 interface Command { public void execute(); } //射击命令 class ShootCommand implements Command{ private Receiver receiver; public ShootCommand(Receiver receiver){ this.receiver = receiver; } public void execute(){ System.out.println("shootCommand is execute:"); receiver.action(); } } //其他命令 class OtherCommand implements Command{ private Receiver receiver; public OtherCommand(Receiver receiver){ this.receiver = receiver; } public void execute(){ System.out.println("otherCommand is execute:"); receiver.action(); } } //命令接受者(士兵) class Receiver { public String name; public Receiver(String name){ this.name = name; } //行动,执行命令 public void action(){ System.out.println(name+" received the command!"); } } //命令调用者(司令官) class Invoker { private List
|
备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
|
个人总结:创建一个备忘录类,用来存储原始类的信息;同时创建备忘录仓库类,用来存储备忘录类,当然,原始类与备忘录类的对应关系要处理好。
|
public class MementoDemo { public static void main(String[] args) { //待备份的类 Originator originator = new Originator(); originator.setState("123"); System.out.println("初始化的状态为:"+originator.getState()); MementoStorage mementoStorage = new MementoStorage(); mementoStorage.add(originator.createMemento()); originator.setState("321"); System.out.println("修改后的状态为:"+originator.getState()); originator.restoreMemento(mementoStorage.get(0)); System.out.println("还原后的状态为:"+originator.getState()); } } //备忘录类 class Memento { private String state; public Memento(String state){ this.state = state; } public String getState(){ return this.state; } } //备忘录类仓库,备忘录管理类 class MementoStorage { private List
|
状态模式:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。
|
个人总结:对象具有多种状态,且每种状态具有特定的行为;应用场景: 行为随状态改变而改变的场景。代码形式似乎也和哪种设计模式相似,还是那句话,设计模式提倡的是思想,而不是形式。
|
public class StateDemo { public static void main(String[] args) { QQContext context = new QQContext(); //设置状态,不同的状态对应不同的行为 context.setState(new OnlineState()); context.getState().getMessage(); } } interface State{ public void getMessage(); } //在线状态(状态对象) class OnlineState implements State{ //在线状态下的行为 public void getMessage(){ System.out.println("在线中,对好友可见!"); } } //隐身状态(状态对象) class StealthState implements State{ //隐身状态下的行为 public void getMessage(){ System.out.println("隐身中,对好友不可见!"); } } //QQ的登陆状态类 class QQContext { private State state; public State getState() { return state; } public void setState(State state) { this.state = state; } }
|
访问者模式:主要将数据结构与数据操作分离。
|
个人总结:在被访问的类里面加一个对外提供接待访问者的接口(如下面例子的accept()方法)。访问者封装了对被访问者结构的一些杂乱操作,避免这些操作"污染"被访问者,解耦结构与算法,同时具有优秀的扩展性。
|
public class Visitor { public static void main(String[] args) { Computer computer = new Computer("myComputer"); //computer接受computerVisitor的访问 computer.accept(new ComputerVisitor()); } } //被访问者 class Computer { private String computerName; public String getComputerName(){ return computerName; } public Computer(String computerName){ this.computerName = computerName; } //提供接待访问者的接口 public void accept(ComputerVisitor computerVisitor){ //访问者访问自身 computerVisitor.visit(this); } } //访问者 class ComputerVisitor { //访问Computer类,将被访问者的引用传入访问者 public void visit(Computer computer){ System.out.println("访问"+computer+"的name属性:"+computer.getComputerName()); } }
|
中介者模式:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
|
个人总结:中介者对象,用来封装关联对象之间的交互操作,使关联对象之间耦合度松散;例如,MVC模式中"控制器"就是"模型"和"视图"的中介者;与适配器模式的区别:适配器模式为了桥接互不兼容的接口,中介者为了分离原始结构和交互行为。
|
public class Mediator { public static void main(String[] args) { User1 user1 = new User1("小明"); User2 user2 = new User2("小红"); UserMediator userMediator = new UserMediator(user1,user2); userMediator.introduceYourselves(); } } class User1 { private String name; public String getName(){ return name; } public User1(String name){ this.name = name; } } class User2 { private String name; public String getName(){ return name; } public User2(String name){ this.name = name; } } //中介者,用来封装User1与User2的交互操作 class UserMediator { private User1 user1; private User2 user2; //将User1与User2传入它们的中介者 public UserMediator(User1 user1, User2 user2){ this.user1 = user1; this.user2 = user2; } public void introduceYourselves(){ System.out.println("Hello "+user1.getName()+",I'm "+user2.getName()); System.out.println("Hi "+user2.getName()+",My name is "+user1.getName()); } }
|
解释器模式:给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。
|
个人总结:自己定义一种语言或表达式(附对应的解释器),用来表示一些复杂的频繁发生的行为。例如:正则表达式的解释,sql语句的解释。使用场景极少,会引起效率、性能、维护等问题。
|
public class Interpreter { public static void main(String[] args) { Context context = new Context(); //创建自定义变量 Variable a = new Variable(); Variable b = new Variable(); //创建常量 3 Constant c = new Constant(3); //给变量赋值 context.put(a, 5).put(b, 1); //构建语法树(自定义表达式:a+b+3) Expression exp = new Add(new Add(a,b), c); //解释表达式a+b+3 int result = exp.interpret(context); System.out.println("a+b+3 = "+result); } } //表达式(所有表达式角色继承该接口,自带解释器) interface Expression { //解释器,解释角色所需参数存储在Context类中 public int interpret(Context context); } //构件环境类,包含解释器之外的一些全局信息,一般是 HashMap。 class Context { private Map
|