7大设计原则+23种设计模式(Java代码版本) 设计原则 具体知识
单一职责原则
一个类=只有一个引起它变化的原因。(只负责担任一个职责 )
如果一个类的职责过多,即耦合度太高=一个职责变化会影响到其他的职责
开放封闭原则
一个实体(类、函数、模块等)应该对外扩展开放,对内修改关闭(这个对应下面的第一点)
1、即每次发生变化时,要通过添加新的代码来增强现有类型的行为,而不是修改原有的代码。 2、符合开放封闭原则的最好方式是提供一个固有的接口,然后让所有可能发生变化的类实现该接口,让固定的接口与相关对象进行交互。
里氏代替原则
子类必须替换掉它们的父类型
1、在软件开发过程中,子类替换父类后,程序的行为是一样的。 2、只有当子类替换掉父类后软件的功能不受影响时,父类才能真正地被复用,而子类也可以在父类的基础上添加新的行为。
依赖倒置原则
细节应该依赖于抽象,而抽象不应该依赖于细节。(高层模块不应该依赖于底层模块,依赖关系应当是面向接口或抽象类,而不是具体实现类) 依赖倒置的本质原则就是 :通过抽象(接口或抽象类)使各个类或模块实现彼此独立,互不影响,实现模块间的松耦合。
每个类尽量都要有接口或抽象类,或者抽象类和接口两者都具备。
变量的显示类型尽量是接口或者抽象类。
任何类尽量不从具体类派生。
尽量不要覆写基类的方法
结合里氏替换原则。 父类出现的地方子类就能出现
所谓的的 “面向接口编程,而不是面向实现编程”。这样可以降低客户与具体实现的耦合。
接口隔离原则
使用多个专门功能的接口,而不是使用单一的总接口。
不要让一个单一的接口承担过多的职责,而应把每个职责分离到多个专门的接口中,进行接口分离。
最少知识原则(迪米特法则)
一个模块或对象应尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立,这样当一个模块修改时,影响的模块就会越少,扩展起来更加容易。
1、关于迪米特法则的其他描述:只与你直接的朋友们通信;不要跟“陌生人”说话。(调用直接关联的类的方法,比如一个类里面关联另外的类,不应该再调用这个类关联的另外的类) 2、外观模式(Facade Pattern)和中介者模式(Mediator Pattern)就使用了迪米特法则。
合成复用原则
在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分。
新对象通过向这些对象的委派达到复用已用功能的目的。简单地说,就是要尽量使用合成/聚合,尽量不要使用继承。
其实还是符合那句在计算机领域很多事情加多一层就能解决问题,例如既然耦合就加层接口
例子 单一职责原则 这个是最简单的,就不放例子了,尽量让类单一实现自己的职责就好了
开闭原则 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 package design_principles;public class DrawingBoard { public static void main (String[] args) { DrawingBoard drawingBoard = new DrawingBoard (new Circle (10 )); drawingBoard.draw(); } private Circle circle; public DrawingBoard (Circle circle) { this .circle = circle; } public void draw () { System.out.println("画一个半径为" + circle.getRadius() + "的圆" ); } } class Circle { private int radius; public Circle (int radius) { this .radius = radius; } public int getRadius () { return radius; } public void setRadius (int radius) { this .radius = radius; } }
优化
当然也可以继承,然后重写新出一个类,替换就好,但是全都是继承也不太好
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 package design_principles.optimize;public class NewDrawingBoard { private Shape shape; public NewDrawingBoard (Shape shape) { this .shape = shape; } public void draw () { shape.draw(); } } class Circle implements Shape { private int radius; public Circle (int radius) { this .radius = radius; } @Override public void draw () { System.out.println("画一个半径为" + radius + "的圆" ); } } class Square implements Shape { private int edge; public Square (int edge) { this .edge = edge; } @Override public void draw () { System.out.println("画一个边长为" + edge + "的方形" ); } } interface Shape { void draw () ; } class Test { public static void main (String[] args) { NewDrawingBoard board = new NewDrawingBoard (new Square (10 )); board.draw(); } }
里氏替换原则 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 public class Rectangle { protected int width; protected int height; public void setWidth (int width) { this .width = width; } public void setHeight (int height) { this .height = height; } public int getArea () { return width * height; } } public class Square extends Rectangle { @Override public void setWidth (int width) { this .width = width; this .height = width; } @Override public void setHeight (int height) { this .height = height; this .width = height; } }
这个设计就可能有潜在的问题,正方形的宽和高应该一定是相等的,但这里的设计我可以让宽为5,长为10
优化,依赖于组合而不是继承,也就是最后一个原则,单独实现一个正方形类就好
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Square { private int side; public void setSide (int side) { this .side = side; } public int getArea () { return side * side; } }
依赖导致原则 这个其实在开闭原则也可以看到,这两个原则经常一起使用
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 package design_principles.dependence_inversion_principle.origin;public class EmailService { public void sendMessage (String message) { } } class SmsService { public void sendMessage (String message) { } } class UserNotification { private EmailService service; public UserNotification (EmailService service) { this .service = service; } public void send (String s) { service.sendMessage(s); } }
优化
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 package design_principles.dependence_inversion_principle.optimize;public interface MessageService { void senMessage (String message) ; } class EmailService implements MessageService { @Override public void senMessage (String message) { } } class SmsService implements MessageService { @Override public void senMessage (String message) { } } class UserNotification { private MessageService messageService; public UserNotification (MessageService messageService) { this .messageService = messageService; } public void sendMessage (String message) { messageService.senMessage(message); } }
接口隔离原则 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 package design_principles.interface_isolation_principle.origin;public interface Animal { void eat () ; void sleep () ; void fly () ; } class Dog implements Animal { @Override public void eat () { } @Override public void sleep () { } @Override public void fly () { } }
优化
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 package design_principles.interface_isolation_principle.optimize;public class Dog implements Eater , Sleeper { @Override public void eat () { } @Override public void sleep () { } } class Bird implements Eater , Sleeper, Flyer { @Override public void eat () { } @Override public void sleep () { } @Override public void fly () { } } interface Eater { void eat () ; } interface Sleeper { void sleep () ; } interface Flyer { void fly () ; }
迪米特法则 最少知道原则,知道调用某个方法能实现就行,而不是知道他有什么属性,调用属性的方法实现
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 public class Student { private String name; public String getName () { return name; } } public class Teacher { private List<Student> students; public List<Student> getStudents () { return students; } } public class School { private List<Teacher> teachers; public List<Teacher> getTeachers () { return teachers; } } public void printStudentNames (School school) { for (Teacher teacher : school.getTeachers()) { for (Student student : teacher.getStudents()) { System.out.println(student.getName()); } } }
上面不符合迪米特法则,直接去获取属性了,应该提供一个方法给外部调用
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 public class Teacher { private List<Student> students; public List<String> getStudentNames () { return students.stream().map(Student::getName).collect(Collectors.toList()); } } public class School { private List<Teacher> teachers; public List<String> getAllStudentNames () { List<String> studentNames = new ArrayList <>(); for (Teacher teacher : teachers) { studentNames.addAll(teacher.getStudentNames()); } return studentNames; } } public void printStudentNames (School school) { for (String name : school.getAllStudentNames()) { System.out.println(name); } }
合成复用原则 优先使用已有代码(组合来实现功能),而不是通过继承来扩展功能
1 2 3 4 5 6 7 public class Printer { public void printText (String text) { System.out.println("Printing text: " + text); } }
这时候要扩展功能,可以借助上面的打印类来完成任务,这时候可以组合两个类,而不是直接继承类来扩展
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class TextProcessor { public String process (String text) { return text.replaceAll("敏感词" , "***" ); } } public class AdvancedPrinter { private Printer printer; private TextProcessor processor; public AdvancedPrinter () { this .printer = new Printer (); this .processor = new TextProcessor (); } public void printText (String text) { String processedText = processor.process(text); printer.printText(processedText); } }
设计模式
创建型(5种,简单工厂不算标准的) 对类的实例化进行抽象。封装了具体类的信息,隐藏了类的实例化过程
单例模式
简单工厂模式
工厂方法模式
抽象工厂模式
建造者模式
原型模式
单例模式(Singleton) 单例类必须自己创建自己的唯一实例:把类的构造方法私有化,内部进行实例化,不让外部调用构造方法实例化 单例类必须给所有其他对象提供这一实例:定义共有方法提供该类全局唯一访问点,外部通过调用getInstance()方法来返回唯一实例
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 package DesignPatterns;public class SingleExample { private static volatile SingleExample instance = null ; private SingleExample () { } public static SingleExample newInstance () { if (instance == null ){ synchronized (SingleExample.class){ if (instance == null ){ instance = new SingleExample (); } } } return instance; } }
简单工厂模式(SimpleFactory Pattern) 通过在工厂类定义一个静态方法负责生产产品对象实例。(类似现实生活中工厂生产产品)
将“类实例化的操作”与“使用对象的操作”分开,让使用者不用知道具体参数就可以实例化出所需要的“产品”类,从而避免了在客户端代码中显式指定,实现了解耦。即使用者可直接消费产品而不需要知道其生产(实例化)的细节。
优点:将创建实例的工作与使用实例的工作分开,使用者不必关心类对象如何创建,实现了解耦; 把初始化实例时的工作放到工厂里进行,使代码更容易维护。 更符合面向对象的原则 & 面向接口编程,而不是面向实现编程。
缺点:工厂类集中了所有实例(产品)的创建逻辑,一旦这个工厂不能正常工作,整个系统都会受到影响; 违背“开放 - 关闭原则”,一旦添加新产品就不得不修改工厂类的逻辑,这样就会造成工厂逻辑过于复杂。 简单工厂模式由于使用了静态工厂方法,静态方法不能被继承和重写,会造成工厂角色无法形成基于继承的等级结构。
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 public abstract class Product { public abstract void show () ; } public class ProductA extends Product { @Override public void show () { System.out.println("生产出A产品" ); } } public class ProductB extends Product { @Override public void show () { System.out.println("生产出B产品" ); } } public class Factory { public static Product product (String type) { switch (type) { case "A" : return new ProductA (); case "B" : return new ProductB (); default : return null ; } } }
工厂方法模式(Factory Method) 工厂方法模式可理解为多态工厂模式,通过定义工厂父类负责定义创建对象的公共接口,而子类则负责生成具体的对象。将类的实例化(具体产品的创建)延迟到工厂类的子类(具体工厂)中完成,即由子类来决定该实例化(创建)哪一个类。
解决了简单工厂的缺点,在保留了简单工厂的封装优点的同时,让扩展变得简单,让继承变得可行,增加了多态性的体现。
(添加新产品时,除了增加新产品类外,还要提供与之对应的具体工厂类,系统类的个数将成对增加,在一定程度上增加了系统的复杂度;同时,有更多的类需要编译和运行,会给系统带来一些额外的开销; 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。 虽然保证了工厂方法内的对修改关闭,但对于使用工厂方法的类,如果要更换另外一种产品,仍然需要修改实例化的具体工厂类;一个具体工厂只能创建一种具体产品)
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 abstract class Product { public abstract void show () ; } public class ProductA extends Product { @Override public void show () { System.out.println("生产出A产品" ); } } public class ProductB extends Product { @Override public void show () { System.out.println("生产出B产品" ); } } public abstract class Factory { public abstract Product produce () ; } public class FactoryA extends Factory { @Override public Product produce () { return new ProductA (); } } public class FactoryB extends Factory { @Override public Product produce () { return new ProductB (); } }
抽象工厂模式(Abastract Factory) 抽象工厂模式,即Abstract Factory Pattern,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类;具体的工厂负责实现具体的产品实例。 抽象工厂模式与工厂方法模式最大的区别:抽象工厂中每个工厂可以创建多种类的产品;而工厂方法每个工厂只能创建一类。 允许使用抽象的接口来创建一组相关产品,而不需要知道或关心实际生产出的具体产品是什么,这样就可以从具体产品中被解耦。 可解决工厂模式缺点:每个工厂只能创建一类产品。
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 interface Button { void render () ; } interface TextField { void input () ; } class WindowsButton implements Button { @Override public void render () { System.out.println("Render a Windows Button" ); } } class WindowsTextField implements TextField { @Override public void input () { System.out.println("Input in a Windows TextField" ); } } class MacOSButton implements Button { @Override public void render () { System.out.println("Render a MacOS Button" ); } } class MacOSTextField implements TextField { @Override public void input () { System.out.println("Input in a MacOS TextField" ); } } interface GUIFactory { Button createButton () ; TextField createTextField () ; } class WindowsFactory implements GUIFactory { @Override public Button createButton () { return new WindowsButton (); } @Override public TextField createTextField () { return new WindowsTextField (); } } class MacOSFactory implements GUIFactory { @Override public Button createButton () { return new MacOSButton (); } @Override public TextField createTextField () { return new MacOSTextField (); } } public class Main { public static void main (String[] args) { GUIFactory windowsFactory = new WindowsFactory (); Button windowsButton = windowsFactory.createButton(); TextField windowsTextField = windowsFactory.createTextField(); windowsButton.render(); windowsTextField.input(); GUIFactory macFactory = new MacOSFactory (); Button macButton = macFactory.createButton(); TextField macTextField = macFactory.createTextField(); macButton.render(); macTextField.input(); } }
从上面可以看出来其实抽象工厂模式就是对工厂方法模式的产品多了一层产品族的抽象
三个工厂模式的对比
简单工厂 : 用来生产同一等级结构中的任意产品。(不支持拓展增加产品,需要动原本的代码,不算拓展增加) 工厂方法 :用来生产同一等级结构中的固定产品。(支持拓展增加产品) 抽象工厂 :用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族,这是为什么呢?是因为每个工厂需要能生产每个产品族的产品,加了一个新种类的产品,不同产品族都有这个产品,Factory类就需要加一个抽象方法生成这类产品,就需要动原本代码了)
建造者模式(Builder Pattern) 适用于一个类内部数据结构过于复杂时(用于很多数据,且组织装配复杂),通过构建者模式可以对类中的数据按部就班地创建与设置。即Builder模式可以将一个类的构建和表示进行分离。 创建者模式又叫建造者模式,是将一个复杂的对象的构建与它的表示分离,使 得同样的构建过程可以创建不同的表示。创建者模式隐藏了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态地创建具有复合属性的对象。
如果一个类构造器需要传入很多参数的时候就可以使用Builder进行重构
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 public class Computer { private String cpu; private String screen; private String memory; private String mainboard; public Computer (String cpu, String screen, String memory, String mainboard) { this .cpu = cpu; this .screen = screen; this .memory = memory; this .mainboard = mainboard; } } public class NewComputer { private String cpu; private String screen; private String memory; private String mainboard; private NewComputer (Builder builder) { this .cpu = builder.cpu; this .screen = builder.screen; this .memory = builder.memory; this .mainboard = builder.mainboard; } public static final class Builder { private String cpu; private String screen; private String memory; private String mainboard; public Builder () { } public Builder cpu (String val) { cpu = val; return this ; } public Builder screen (String val) { screen = val; return this ; } public Builder memory (String val) { memory = val; return this ; } public Builder mainboard (String val) { mainboard = val; return this ; } public NewComputer build () { return new NewComputer (this ); } } public static void main (String[] args) { NewComputer newComputer = new Builder () .cpu("" ) .screen("" ) .memory("" ) .mainboard("" ) .build(); } }
原型模式(Prototype Pattern) 原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能,(借助已经创建的实例作为原型,创建相同的新对象)
使用时机
类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等,通过原型拷贝避免这些消耗。 通过new产生一个对象需要非常繁琐的数据准备或访问权限,这时可以使用原型模式。 一个对象需要供给其他对象访问,而且各个对象都需要修改其值时,可以拷贝多个对象供调用者访问,即保护性拷贝。
并且通过clone(java的clone方法就是在应用原型设计模式)可以不依赖于这个类的具体属性的实现和初始化
结构
抽象原型类:规定访问原型对象实现具体的clone方法
具体原型类:实现抽象类的clone方法,它是可以被复制的对象
访问类:调用具体原型中的clone方法来复制新的对象
浅拷贝和深拷贝 浅拷贝又叫影子拷贝,上面我们在拷贝文档时并没有把原文档中的字段都重新构造了一遍,而只是拷贝了引用,也就是副文档的字段引用原始文档的字段,这样的话修改副文档中的内容就会连原始文档也改掉了,这就是浅拷贝 深拷贝就是在浅拷贝的基础上,对于引用类型的字段也要采用拷贝的形式,比如上面的images,而像String、int这些基本数据类型则没关系
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 import java.util.HashMap;import java.util.Map;interface Shape extends Cloneable { Shape clone () ; void draw () ; } class Circle implements Shape { private int radius; public Circle (int radius) { this .radius = radius; System.out.println("Initializing a Circle with radius: " + radius); } @Override public Circle clone () { try { return (Circle) super .clone(); } catch (CloneNotSupportedException e) { throw new AssertionError (); } } @Override public void draw () { System.out.println("Drawing a Circle with radius: " + radius); } } class Rectangle implements Shape { private int width; private int height; public Rectangle (int width, int height) { this .width = width; this .height = height; System.out.println("Initializing a Rectangle with width: " + width + ", height: " + height); } @Override public Rectangle clone () { try { return (Rectangle) super .clone(); } catch (CloneNotSupportedException e) { throw new AssertionError (); } } @Override public void draw () { System.out.println("Drawing a Rectangle with width: " + width + ", height: " + height); } } class ShapePrototypeManager { private Map<String, Shape> shapeMap = new HashMap <>(); public void registerShape (String key, Shape shape) { shapeMap.put(key, shape); } public Shape getShape (String key) { Shape shape = shapeMap.get(key); return shape != null ? shape.clone() : null ; } } public class Main { public static void main (String[] args) { ShapePrototypeManager prototypeManager = new ShapePrototypeManager (); Circle circle = new Circle (5 ); Rectangle rectangle = new Rectangle (10 , 20 ); prototypeManager.registerShape("circle" , circle); prototypeManager.registerShape("rectangle" , rectangle); Shape clonedCircle = prototypeManager.getShape("circle" ); Shape clonedRectangle = prototypeManager.getShape("rectangle" ); clonedCircle.draw(); clonedRectangle.draw(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public Object clone () { return new Intent (this ); } private Intent (Intent o, boolean all) { this .mAction = o.mAction; this .mData = o.mData; this .mType = o.mType; this .mPackage = o.mPackage; this .mComponent = o.mComponent; if (o.mCategories != null ) { this .mCategories = new ArraySet <String>(o.mCategories); } }
我们常用的Intent,ArrayList等都实现clone 登录模块中保存的用户信息类需要通过服务器更新用户信息,但是有很多地方需要调用,需要设置为对其他用到的模块只读,这个时候可以考虑用原型模式进行保护性拷贝
原型设计模式解决对象创建开销大、构建复杂等问题,并且深拷贝和浅拷贝都符合这个设计模式,看具体业务需求,java引用类要做到深拷贝就实现Clonable接口实现clone方法
结构型(7种) 结构型模式描述如何将类和对象按某种布局组织成更大的结构,分为结构型设计模式和对象结构型设计模式,前者采用继承机制来组织类和接口,后者采用组合或聚合组合对象,对象型满足合成复用原则,具有更大的灵活性
代理模式
适配器模式
装饰者模式
桥接模式
外观模式
组合模式
享元模式
代理模式(Proxy Pattern) 适配器模式(Adapter) 系统需要使用现有的类,而此类的接口不符合系统的需要,即接口不兼容 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的一些类一起工作 需要一个统一的输出接口,而输入端的接口不可预知
优点: 1、可以让任何两个没有关联的类一起运行。 2、提高了类的复用。 3、增加了类的透明度。 4、灵活性好。
缺点: 1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。 2.由于 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 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 public interface MediaPlayer { public void play (String audioType,String fileName) ; } interface AdvancedMediaPlayer { public void playVlc (String fileName) ; public void playMp4 (String fileName) ; } public class VlcPlayer implements AdvancedMediaPlayer { @Override public void playVlc (String fileName) { System.out.println("播放vlc文件,名字为" + fileName); } @Override public void playMp4 (String fileName) { } } class Mp4Player implements AdvancedMediaPlayer { @Override public void playVlc (String fileName) { } @Override public void playMp4 (String fileName) { System.out.println("播放mp4文件,名字为" + fileName); } } public class MediaAdapter implements MediaPlayer { private AdvancedMediaPlayer advancedMediaPlayer; public MediaAdapter (String audioType) { if (audioType.equalsIgnoreCase("vlc" )){ advancedMediaPlayer = new VlcPlayer (); } else if (audioType.equalsIgnoreCase("mp4" )) { advancedMediaPlayer = new Mp4Player (); } } @Override public void play (String audioType, String fileName) { if (audioType.equalsIgnoreCase("vlc" )){ advancedMediaPlayer.playVlc(fileName); } else if (audioType.equalsIgnoreCase("mp4" )) { advancedMediaPlayer.playMp4(fileName); } } } public class AudioPlayer implements MediaPlayer { private MediaAdapter mediaAdapter; @Override public void play (String audioType, String fileName) { if (audioType.equalsIgnoreCase("mp3" )){ System.out.println("Playing mp3 file. Name: " + fileName); } else if (audioType.equalsIgnoreCase("vlc" ) || audioType.equalsIgnoreCase("mp4" )){ mediaAdapter = new MediaAdapter (audioType); mediaAdapter.play(audioType, fileName); } else { System.out.println("Invalid media. " + audioType + " format not supported" ); } } }
看的有点懵逼,回头再看
看的有点懵逼,回头再看
动态的给一个对象添加一些额外的职责。就增加功能来说,装饰模式比生成子类(继承)更为灵活的方案。
装饰模式主要在于扩展了类的功能。装饰模式通过在被装饰组件的方法执行之前或之后加入新的方法来实现功能的扩展.
具体应用:Android源码中的ContextWrapper
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 public interface Shape { void draw () ; } public class Rectangle implements Shape { @Override public void draw () { System.out.println("Shape: Rectangle" ); } } class Circle implements Shape { @Override public void draw () { System.out.println("Shape: Circle" ); } } public abstract class ShapeDecorator implements Shape { protected Shape decoratedShape; public ShapeDecorator (Shape decoratedShape) { this .decoratedShape = decoratedShape; } @Override public void draw () { decoratedShape.draw(); } } public class RedShapeDecorator extends ShapeDecorator { public RedShapeDecorator (Shape decoratedShape) { super (decoratedShape); } @Override public void draw () { decoratedShape.draw(); setRedBorder(decoratedShape); } private void setRedBorder (Shape decoratedShape) { System.out.println("Border Color: Red" ); } } public class DecoratorPatternDemo { public static void main (String[] args) { Shape circle = new Circle (); ShapeDecorator redCircle = new RedShapeDecorator (new Circle ()); ShapeDecorator redRectangle = new RedShapeDecorator (new Rectangle ()); System.out.println("Circle with normal border" ); circle.draw(); System.out.println("\nCircle of red border" ); redCircle.draw(); System.out.println("\nRectangle of red border" ); redRectangle.draw(); } }
外观模式(Facade Pattern)隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。 这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。 用于为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
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 public interface Camera { public void open () ; public void takePhoto () ; public void close () ; } class MiCamera implements Camera { @Override public void open () { System.out.println("打开相机" ); } @Override public void takePhoto () { System.out.println("拍照" ); } @Override public void close () { System.out.println("关闭相机" ); } } public interface Phone { public void dail () ; public void hangup () ; } class PhoneImpl implements Phone { @Override public void dail () { System.out.println("打电话" ); } @Override public void hangup () { System.out.println("挂断" ); } } public class MobilePhone { private Phone mPhone = new PhoneImpl (); private Camera mCamera = new MiCamera (); public void takePhoto () { mCamera.open(); mCamera.takePhoto(); mCamera.close(); } public void videoChat () { mCamera.open(); mPhone.dail(); } public static void main (String[] args) { MobilePhone mobilePhone = new MobilePhone (); mobilePhone.takePhoto(); mobilePhone.videoChat(); } }
应用:很多的第三方SDK,比如友盟统计;我们平时开发过程中封装的模块,比如网络模块、ImageLoader模块等
行为型
模板方法模式
观察者模式
状态模式
策略模式
责任链模式
模板方法模式(Template Method) 定义一个模板结构,将具体内容延迟到子类去实现。在不改变模板结构的前提下在子类中重新定义模板中的内容。 模板方法模式是基于”继承“的;
角色
说明
抽象主题/被观察者(Observable)
抽象主题把所有的观察者对象的引用保存在一个集合里,每个主题可以有任意数量的观察者,抽象主题提供接口,可以增加和删除观察者对象
具体的主题(具体的被观察者)
也就是抽象主题的子类,该角色将有关状态存入具体观察者对象,在具体主题内部状态发生改变时,通知所有注册过的观察者
抽象观察者
观察者的抽象类,定义了一个更新的接口
具体观察者
实现了抽象观察者的更新接口,在被观察者状态发生变化时更新自身的状态
观察者模式通过将主题和观察者解耦,实现了对象之间的松耦合。当主题的状态发生改变时,所有依赖于它的观察者都会收到通知并进行相应的更新。
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 public class Subject { private List<Observer>observers = new ArrayList <>(); private int state; public int getState () { return state; } public void setState (int state) { this .state = state; notifyAllObservers(); } public void attach (Observer observer) { observers.add(observer); } public void notifyAllObservers () { for (Observer observer:observers){ observer.update(); } } } public abstract class Observer { protected Subject subject; public abstract void update () ; } public class BinaryObserver extends Observer { public BinaryObserver (Subject subject) { this .subject = subject; this .subject.attach(this ); } @Override public void update () { System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) ); } } class OctalObserver extends Observer { public OctalObserver (Subject subject) { this .subject = subject; this .subject.attach(this ); } @Override public void update () { System.out.println("Octal String: " + Integer.toOctalString(subject.getState())); } } class HexaObserver extends Observer { public HexaObserver (Subject subject) { this .subject = subject; this .subject.attach(this ); } @Override public void update () { System.out.println( "Hex String: " + Integer.toHexString( subject.getState() ).toUpperCase() ); } } public class ObserverPatternDemo { public static void main (String[] args) { Subject subject = new Subject (); new HexaObserver (subject); new OctalObserver (subject); new BinaryObserver (subject); System.out.println("First state change: 15" ); subject.setState(15 ); System.out.println("Second state change: 10" ); subject.setState(10 ); } }
在状态模式(State Pattern)中,类的行为是基于它的状态改变的。这种类型的设计模式属于行为型模式。在状态模式中,我们创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象。 当代码中包含着大量与对象状态有关的条件语句。此时对象行为依赖于它的状态(属性),并可以根据它的状态改变它的相关行为。
优点: 1、封装了转换规则。 2、枚举可能的状态,在枚举状态之前需要确定状态种类。 3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。 4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。 5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点: 1、状态模式的使用必然会增加系统类和对象的个数。 2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。 3、状态模式对”开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
使用场景: 1、行为随状态改变而改变的场景。 2、条件、分支语句的代替者。
注意事项:在行为受状态约束的时候使用状态模式,而且状态不超过 5 个。
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 public interface UserState { void forward () ; void comment () ; } public class LoginState implements UserState { @Override public void forward () { System.out.println("转发成功" ); } @Override public void comment () { System.out.println("评论成功" ); } } class UnLoginState implements UserState { @Override public void forward () { System.out.println("未登录" ); } @Override public void comment () { System.out.println("未登录" ); } } public class LoginContext { private UserState userState = new UnLoginState (); static LoginContext loginContext = new LoginContext (); private LoginContext () { } public static LoginContext getLoginContext () { return loginContext; } public void setUserState (UserState userState) { this .userState = userState; } public void forward () { userState.forward(); } public void comment () { userState.comment(); } public static void main (String[] args) { LoginContext loginContext1 = getLoginContext(); loginContext1.comment(); loginContext1.setUserState(new LoginState ()); loginContext1.comment(); } }
状态模式和策略模式的结构几乎完全一样,但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、可相互替换的。用一句话来表述,状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态发生改变的时候,其行为也随之改变
状态模式:状态模式关注对象的内部状态改变,并相应地改变对象的行为。它将状态的管理和对象的行为解耦,使得状态变化不会导致对象行为的复杂性增加。 策略模式:策略模式关注对象的行为的变化,并提供了一种灵活的方式来选择不同的行为或算法。它将算法的选择和使用从对象中解耦,使得算法的变化不会影响到对象的使用者。
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 public interface CalculateStragety { int calculatePrice (int km) ; } public class BusStragety implements CalculateStragety { @Override public int calculatePrice (int km) { int extraTotal = km - 10 ; int extraFactor = extraTotal / 5 ; int fraction = extraTotal % 5 ; int price = 1 + extraFactor * 1 ; return fraction > 0 ? ++price : price; } } class TaxiStragety implements CalculateStragety { @Override public int calculatePrice (int km) { return km * 2 ; } } public class TrafficCalculator { public static void main (String[] args) { TrafficCalculator trafficCalculator = new TrafficCalculator (); trafficCalculator.setCalculateStragety(new BusStragety ()); trafficCalculator.calculatePrice(66 ); } CalculateStragety mCalculateStragety; public void setCalculateStragety (CalculateStragety calculateStragety) { mCalculateStragety = calculateStragety; } private int calculatePrice (int km) { return mCalculateStragety.calculatePrice(km); } }
责任链模式(Chain of Responsibility Pattern)
职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。
优点: 1、降低耦合度。它将请求的发送者和接收者解耦。 2、简化了对象。使得对象不需要知道链的结构。 3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。 4、增加新的请求处理类很方便。
缺点: 1、不能保证请求一定被接收。 2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。 3、可能不容易观察运行时的特征,有碍于除错。
使用场景: 1、有多个对象可以处理同一个请求,具体哪个对象处理该请求由运行时刻自动确定。 2、在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。 3、可动态指定一组对象处理请求。
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 public abstract class AbstractLogger { public static int INFO = 1 ; public static int DEBUG = 2 ; public static int ERROR = 3 ; protected int level; protected AbstractLogger nextLogger; public void setNextLogger (AbstractLogger nextLogger) { this .nextLogger = nextLogger; } public void logMessage (int level, String message) { if (this .level <= level){ write(message); } if (nextLogger !=null ){ nextLogger.logMessage(level, message); } } abstract protected void write (String message) ; } public class ConsoleLogger extends AbstractLogger { public ConsoleLogger (int level) { this .level = level; } @Override protected void write (String message) { System.out.println("Standard Console::Logger: " + message); } } class ErrorLogger extends AbstractLogger { public ErrorLogger (int level) { this .level = level; } @Override protected void write (String message) { System.out.println("Error Console::Logger: " + message); } } class FileLogger extends AbstractLogger { public FileLogger (int level) { this .level = level; } @Override protected void write (String message) { System.out.println("File::Logger: " + message); } } public class ChainPatternDemo { private static AbstractLogger getChainOfLoggers () { AbstractLogger errorLogger = new ErrorLogger (AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger (AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger (AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main (String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "This is an information." ); loggerChain.logMessage(AbstractLogger.DEBUG, "This is a debug level information." ); loggerChain.logMessage(AbstractLogger.ERROR, "This is an error information." ); } }