文章目录
GitHub:shpunishment/design-pattern-test
1. 原则
开闭原则(Open Close Principle)
软件应该对扩展开放,而对修改关闭。在增加新功能的时候,能不改代码就尽量不要改。这是为了提高程序的扩展性,易于维护和升级。想要达到这样的效果,需要使用接口和抽象类。
里氏替换原则(Liskov Substitution Principle)
面向对象设计的基本原则之一。任何父类可以出现的地方,子类一定可以出现。即如果我们调用一个父类的方法可以成功,那么替换成子类调用也应该完全可以运行。
里氏替换原则是对开闭原则的补充。实现开闭原则的关键步骤就是抽象化,而父类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范。
依赖倒转原则(Dependence Inversion Principle)
开闭原则的基础,针对接口编程,依赖于抽象而不依赖于具体。
接口隔离原则(Interface Segregation Principle)
使用多个隔离的接口,比使用单个接口要好。即降低类之间的耦合度。
迪米特法则(Demeter Principle)
又称最少知道原则。一个实体应当尽量少地与其他实体之间发生相互作用,使得系统功能模块相对独立。
合成复用原则(Composite Reuse Principle)
尽量使用合成/聚合的方式,而不是使用继承。
2. 创建型模式
创建型模式关注如何创建对象,把对象的创建和使用相分离,这样使得两者能相对独立地变换。
2.1 简单工厂模式
简单工厂模式通常只有一个工厂类,里面有一个静态方法,根据不同的参数,返回不同的派生自同一个父类(或实现同一接口)的实例对象。
public class SimpleFactory {
public static Food getFood(String name) {
if (name.equals("A")) {
return new FoodA();
} else if (name.equals("B")) {
return new FoodB();
}
return null;
}
}
2.2 工厂模式
工厂模式(Factory)定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
工厂模式在简单工厂模式的基础上增加了选择工厂的维度,需要先选择合适的工厂。
public class Main {
public static void main(String[] args) {
FoodFactory chineseFoodFactory = new ChineseFoodFactory();
Food chineseFoodA = chineseFoodFactory.getFood("A");
FoodFactory americanFoodFactory = new AmericanFoodFactory();
Food americanFoodB = americanFoodFactory.getFood("B");
}
}
2.3 抽象工厂模式
抽象工厂模式(Abstract Factory)提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式和工厂模式不太一样,它要解决的问题比较复杂。不但工厂和产品是抽象的,而且有多个产品需要创建。这个抽象工厂会对应到多个实际工厂,每个实际工厂负责创建多个实际产品,类似于多个供应商负责提供一系列类型的产品。使得创建工厂和一组产品与使用相分离,并可以随时切换到另一个工厂以及另一组产品。抽象工厂模式有产品族的概念,适合各个产品存在兼容性问题时使用。
public class Main {
public static void main(String[] args) {
ComputerFactory computerAFactory = new ComputerAFactory();
Cpu cpuA = computerAFactory.getCpu();
MainBoard mainBoardA = computerAFactory.getMainBoard();
Computer computer = new Computer(cpuA, mainBoardA);
}
}
2.4 建造者模式
建造者模式(Builder)将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式适合属性很多的类。创建一个复杂的对象,需要多个步骤完成创建,或者需要多个零件组装的场景,且创建过程中可以灵活调用不同的步骤或组件。
public class User
private String id;
private String name;
private User(String id, String name) {
this.id = id;
this.name = name;
}
public static UserBuilder builder() {
return new UserBuilder();
}
public static class UserBuilder {
private String id;
private String name;
private UserBuilder(){}
public UserBuilder id(String id) {
this.id = id;
return this;
}
public UserBuilder name(String name) {
this.name = name;
return this;
}
public User build() {
return new User(this.id, this.name);
}
}
}
2.5 原型模式
原型模式(Prototype)用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
当创建新对象的时候,根据一个现有对象实例复制出一个新的实例,复制出的类型和属性与原实例相同。Java的Object提供了一个clone()方法,能够复制一个新的对象出来,Cloneable接口来标识一个对象是可复制的。
public class User implements Cloneable {
private String id;
private String name;
public User(String id, String name) {
this.id = id;
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
2.6 单例模式
单例模式(Singleton)保证一个类仅有一个实例,并提供一个访问它的全局访问点,即全局使用的是同一个对象。既保证了安全,也节省资源。
饿汉式
public class Singleton1 {
private Singleton1() {}
private static Singleton1 instance = new Singleton1();
public static Singleton1 getInstance() {
return instance;
}
}
饱汉式/双重校验锁(double-checked locking,DCL)
public class Singleton2 {
private Singleton2(){}
private volatile static Singleton2 instance;
public static Singleton2 getInstance() {
if (instance == null) {
synchronized (Singleton2.class) {
if (instance == null) {
instance = new Singleton2();
}
}
}
return instance;
}
}
静态内部类
public class Singleton3 {
private Singleton3(){}
private static class Holder {
private static Singleton3 instance = new Singleton3();
}
public static Singleton3 getInstance() {
return Holder.instance;
}
}
3. 结构型模式
结构型模式关注如何组合各种对象以便获得更好、更灵活的结构。虽然面向对象的继承机制提供了最基本的子类扩展父类的功能,但结构型模式不仅仅简单地使用继承,而更多地通过组合与运行期的动态组合来实现更灵活的功能。
3.1 适配器模式
适配器模式(Adapter,也称Wrapper)将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。如果一个接口需要B接口,但是待传入的对象却是A接口时,可以将一个A接口转换为B接口,使得新的对象符合B接口规范。
public class Main {
public static void main(String[] args) {
UserAServiceImpl userAService = new UserAServiceImpl("id A", "name A");
UserBAdapter userBAdapter = new UserBAdapter(userAService);
userBAdapter.setUserBId("id B adapter");
userBAdapter.setUserBName("name B adapter");
}
}
3.2 桥接模式
桥接模式(Bridge)将抽象部分与实现部分分离,使它们都可以独立的变化。通过分离一个抽象接口和它的实现部分,使得设计可以按两个维度独立扩展,做到了很好的解耦。适用于两两组合的场景,防止子类爆炸。
public class Main {
public static void main(String[] args) {
Car carA1 = new CarA(new Engine1());
carA1.drive();
Car carA2 = new CarA(new Engine2());
carA2.drive();
Car carA3 = new CarA(new Engine3());
carA3.drive();
Car carB1 = new CarB(new Engine1());
carB1.drive();
}
}
3.3 组合模式
组合模式(Composite)将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。常用于树形结构,可以把一个叶子节点与一个父节点统一起来处理。使得叶子对象和容器对象具有一致性,从而形成统一的树形结构,并用一致的方式去处理它们。
public class Node {
private String name;
private List<Node> nodeList;
public Node(String name) {
this.name = name;
nodeList = new ArrayList<>();
}
}
3.4 装饰器模式
装饰器模式(Decorator)动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活。它可以独立增加核心功能,也可以独立增加附加功能,二者互不影响。适用于装饰类或增强类的场景。
public class Main {
public static void main(String[] args) {
Car car1 = new CarDecorator1(new CarA());
System.out.println(car1.getCar());
Car car2 = new CarDecorator2(new CarDecorator1(new CarB()));
System.out.println(car2.getCar());
}
}
3.5 门面模式
门面/外观模式(Facade)为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。即为了给客户端提供一个统一入口,并对外屏蔽内部子系统的调用细节。
3.6 享元模式
享元模式(Flyweight)运用共享技术有效地支持大量细粒度的对象。如果一个对象实例一经创建就不可变,那么反复创建相同的实例就没有必要,直接向调用方返回一个共享的实例就行,这样即节省内存,又可以减少创建对象的过程,提高运行速度。
public class User {
private String id;
private String name;
private static Map<String, User> cacheMap = new HashMap<>();
private User(String id, String name){
this.id = id;
this.name = name;
}
public static User create(String id, String name) {
String key = id + "#" + name;
User user;
if (cacheMap.containsKey(key)) {
user = cacheMap.get(key);
} else {
user = new User(id, name);
cacheMap.put(key, user);
}
return user;
}
}
3.7 代理模式
代理模式(Proxy)为其他对象提供一种代理以控制对这个对象的访问。通过封装一个已有接口,并向调用方返回相同的接口类型,能让调用方在不改变任何代码的前提下进行方法增强,增强某些功能(如鉴权、延迟加载、连接池复用等)。
public class Main {
public static void main(String[] args) {
UserService userServiceProxy = new UserServiceProxy();
userServiceProxy.getId();
userServiceProxy.getName();
}
}
4. 行为型模式
行为型模式关注算法和对象间的职责分配。通过使用对象组合,行为型模式可以描述一组对象应该如何协作来完成一个整体任务。
4.1 责任链模式
责任链模式(Chain of Responsibility)使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。使得添加新的处理器或者重新排列处理器都很容易,常用于拦截、预处理请求等。
public class Main {
public static void main(String[] args) {
AbstractChain chain1 = new Chain1();
AbstractChain chain2 = new Chain2();
AbstractChain chain3 = new Chain3();
chain1.setNext(chain2);
chain2.setNext(chain3);
chain1.apply(2);
}
}
4.2 命令模式
命令模式(Command)将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化。即把请求封装成一个命令,然后执行该命令。将命令的创建和执行分离,使得调用者无需关心具体的执行过程。通过封装命令对象,使命令模式可以保存已执行的命令,从而支持撤销、重做等操作。
public class Main {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
AddCommand addCommand = new AddCommand(userService);
addCommand.execute();
addCommand.execute();
UpdateCommand updateCommand = new UpdateCommand(userService);
updateCommand.execute();
DeleteCommand deleteCommand = new DeleteCommand(userService);
deleteCommand.execute();
}
}
4.3 解释器模式
解释器模式(Interpreter)给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。通过抽象语法树实现对用户输入的解释执行。
public class Main {
public static void main(String[] args) {
String str = "+18611112222";
System.out.println(str.matches("\\+\\d+$"));
}
}
4.4 迭代器模式
迭代器模式(Iterator)提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。常用于遍历集合,它允许集合提供一个统一的迭代器接口来遍历元素,同时保证调用者对集合内部的数据结构一无所知,从而使得调用者总是以相同的接口遍历各种不同类型的集合。
public class Main {
public static void main(String[] args) {
UserContainer userContainer = new UserContainer(10);
userContainer.add(new User("1"));
userContainer.add(new User("2"));
userContainer.add(new User("3"));
for (Iterator iterator = userContainer.iterator(); iterator.hasNext();) {
User user = (User)iterator.next();
System.out.println(user);
}
}
}
4.5 中介模式
中介模式(Mediator)用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。通过引入一个中介对象,把多边关系变成多个双边关系,从而简化系统组件的交互耦合度。
public class Main {
public static void main(String[] args) {
User user1 = new User("1");
User user2 = new User("2");
user1.sendMsg("111");
user2.sendMsg("222");
}
}
4.6 备忘录模式
备忘录模式(Memento)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。主要用于捕获一个对象的内部状态,以便在将来的某个时候恢复此状态。
public class Main {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator originator = new Originator();
originator.setState("#1");
originator.setState("#2");
caretaker.save(originator.saveStateToMemento());
originator.setState("#3");
caretaker.save(originator.saveStateToMemento());
originator.setState("#4");
System.out.println("current: " + originator.getState());
originator.getStateFromMemento(caretaker.get(0));
System.out.println("current: " + originator.getState());
originator.getStateFromMemento(caretaker.get(1));
System.out.println("current: " + originator.getState());
}
}
4.7 观察者模式
观察者模式(Observer),也称也称发布-订阅模式(Publish-Subscribe:Pub/Sub)。定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。它是一种一对多通知机制,让发送通知的一方(被观察方)和接收通知的一方(观察者)能彼此分离,互不影响。
public class Main {
public static void main(String[] args) {
Subject subject = new Subject();
Observer observer1 = new Observer1(subject);
Observer observer2 = new Observer2(subject);
subject.update("data");
}
}
4.8 状态模式
状态模式(State)允许一个对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类。它描述了对象状态的变化以及对象如何在每一种状态下表现出不同的行为,常用在带有状态的对象中。把不同状态的逻辑分离到不同的状态类中,从而使得增加新状态更容易。实现关键在于状态转换。简单的状态转换可以直接由调用方指定,复杂的状态转换可以在内部根据条件触发完成。
public class Main {
public static void main(String[] args) {
Context context = new Context();
StartState startState = new StartState();
startState.doAction(context);
System.out.println(context.getState());
EndState endState = new EndState();
endState.doAction(context);
System.out.println(context.getState());
}
}
4.9 策略模式
策略模式(Strategy)定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换,使得算法可独立于使用它的客户而变化。允许调用方选择一个算法,从而通过不同策略实现不同的计算结果。通过扩展策略,不必修改主逻辑,即可获得新策略的结果。
public class Main {
public static void main(String[] args) {
Context context = new Context(new Strategy1());
context.execute();
}
}
4.10 模板模式
模板模式(Template)定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。模板模式是一种高层定义骨架,底层实现细节的设计模式,适用于流程固定,但某些步骤不确定或可替换的场景。
public class Main {
public static void main(String[] args) {
Template template1 = new Template1();
template1.run();
Template template2 = new Template2();
template2.run();
}
}
4.11 访问者模式
访问者模式(Visitor)主要将数据结构与数据操作分离。它是一种操作一组对象的操作,它的目的是不改变对象的定义,但允许新增不同的访问者,来定义新的操作。它不区分元素,根据访问者不同信息返回相应的信息。
public class Main {
public static void main(String[] args) {
ComputerPart compute = new Computer();
compute.accept(new ComputerVisitor());
}
}