设计模式
什么是设计模式?
设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。他不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
学习设计模式的意义
设计模式的本质是面向对象设计原则的实际运用,是对类的封装性、继承性和多态性以及类的关联关系和组合关系的充分理解。
正确使用设计模式具有以下优点:
- 可以提高程序员的思维能力、编程能力、和设计能力。
- 使用设计更加标准化,代码编制更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
- 使代码的可重用性高、可读性强、可靠性高、灵活性号、可维护性强。
GoF23
GoF23
创建型模式:
- 单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
结构型模式:
- 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
行为型模式:
- 模仿方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式
设计模式的基本要素
模式名称
问题
解决方法
效果
OOP七大原则
开闭原则:对扩展开放,对修改关闭
里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
依赖倒置原则:要面向接口编程,不要面向实现编程
单一职责原则:控制类的粒度大小,将对象解耦、提高其内聚性
接口隔离原则:要为各个类建立它们需要的专用接口
迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话
合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现
单例模式
Ensure a class has only one instance, and provide a global point of access to it.
确保一个类只有一个实例,而且自行实例化并向整个系统提供这个实例
工厂模式
作用
OOP七大原则
- 开闭原则:对扩展开放,对修改关闭
- 依赖倒置原则:要面向接口编程,不要面向实现编程
- 迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话
核心本质:
- 实例化对象不适用new,用工厂方法代替
- 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
三种模式:
- 简单工厂模式
- 用来生产同一等级结构中的任意产品(对于增加新的产品,需要扩展已有代码)
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂
简单工厂模式
1 2 3 4 5 6
| package UML.factory.simple;
public interface Car { void name(); }
|
1 2 3 4 5 6 7 8
| package UML.factory.simple;
public class WuLing implements Car{ @Override public void name() { System.out.println("五菱宏光"); } }
|
1 2 3 4 5 6 7 8
| package UML.factory.simple;
public class Tesla implements Car{ @Override public void name() { System.out.println("特斯拉"); } }
|
1 2 3 4 5 6 7 8
| package UML.factory.simple;
public class Dazhong implements Car{ @Override public void name() { System.out.println("大众"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package UML.factory.simple;
public class CarFactory { public static Car getCar(String car){ if(car.equals("五菱")){ return new WuLing(); } else if (car.equals("特斯拉")) { return new Tesla(); }else { return null; } } public static Car getWuLing(){ return new WuLing(); }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package UML.factory.simple;
public class Constumer { public static void main(String[] args) {
Car car = CarFactory.getCar("五菱"); Car car2 = CarFactory.getCar("特斯拉");
car.name(); car2.name(); } }
|
工厂方法模式
1 2 3 4 5
| package UML.factory.method;
public interface Car { void name(); }
|
1 2 3 4 5 6 7 8
| package UML.factory.method;
public class WuLing implements Car { @Override public void name() { System.out.println("五菱宏光"); } }
|
1 2 3 4 5 6 7 8
| package UML.factory.method;
public class Tesla implements Car { @Override public void name() { System.out.println("特斯拉"); } }
|
1 2 3 4 5 6 7 8 9
| package UML.factory.method;
public class MoBai implements Car { @Override public void name() { System.out.println("摩拜单车"); } }
|
1 2 3 4 5 6
| package UML.factory.method;
public interface CarFactory { Car getCar(); }
|
1 2 3 4 5 6 7 8
| package UML.factory.method;
public class WuLingFactory implements CarFactory{ @Override public Car getCar() { return new WuLing(); } }
|
1 2 3 4 5 6 7 8
| package UML.factory.method;
public class TeslaFactory implements CarFactory{ @Override public Car getCar() { return new Tesla(); } }
|
1 2 3 4 5 6 7 8
| package UML.factory.method;
public class MoBaiFactory implements CarFactory{ @Override public Car getCar() { return new MoBai(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package UML.factory.method;
import UML.factory.simple.CarFactory;
public class Constumer { public static void main(String[] args) { Car car = new WuLingFactory().getCar(); Car car2 = new TeslaFactory().getCar(); Car car3 = new MoBaiFactory().getCar();
car.name(); car2.name(); car3.name(); }
}
|
小结:
- 简单工厂模式(静态工厂模式)
- 工厂方法模式
- 抽象工厂模式
应用场景:
- JDK中Calendar的getlnstance方法
- JDBC中的Connection对象的获取
- Spring中IOC容器创建管理bean对象
- 反射中Class对象的newlnstance方法
抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
适用场景:
客户端(应用层)不依赖产品类实例如何被创建、实现等细节
强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
所有同一产品族里的东西都是由一个工厂生产的,但位于不同的等级结构
1 2 3 4 5 6 7 8 9
| package UML.factory.abstract1;
public interface IphoneProduct { void start(); void shutdown(); void callup(); void sendSMS(); }
|
1 2 3 4 5 6 7 8 9
| package UML.factory.abstract1;
public interface IRouterProduct { void start(); void shutdown(); void openwifi(); void setting(); }
|
1 2 3 4 5 6 7 8 9 10 11
| package UML.factory.abstract1;
public interface IProductFactory { IphoneProduct iphoneProduct();
IRouterProduct irouterProduct();
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package UML.factory.abstract1;
public class XiaomiPhone implements IphoneProduct{ @Override public void start() { System.out.println("开启小米手机"); }
@Override public void shutdown() { System.out.println("关闭小米手机"); }
@Override public void callup() { System.out.println("小米打电话"); }
@Override public void sendSMS() { System.out.println("小米发短信"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class XiaomiRouter implements IRouterProduct{ @Override public void start() { System.out.println("启动小米路由器"); }
@Override public void shutdown() { System.out.println("关闭小米路由器"); }
@Override public void openwifi() { System.out.println("打开小米WIFI"); }
@Override public void setting() { System.out.println("小米设置"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package UML.factory.abstract1;
public class HuaweiPhone implements IphoneProduct{ @Override public void start() { System.out.println("开启华为手机"); }
@Override public void shutdown() { System.out.println("关闭华为手机"); }
@Override public void callup() { System.out.println("华为打电话"); }
@Override public void sendSMS() { System.out.println("华为发短信"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package UML.factory.abstract1;
public class HuaweiRouter implements IRouterProduct{ @Override public void start() { System.out.println("启动华为路由器"); }
@Override public void shutdown() { System.out.println("关闭华为路由器"); }
@Override public void openwifi() { System.out.println("打开华为WIFI"); }
@Override public void setting() { System.out.println("华为设置"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| package UML.factory.abstract1;
public class XiaomiFactory implements IProductFactory{ @Override public IphoneProduct iphoneProduct() { return new XiaomiPhone(); }
@Override public IRouterProduct irouterProduct() { return new XiaomiRouter(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| package UML.factory.abstract1;
public class HuaweiFactory implements IProductFactory{ @Override public IphoneProduct iphoneProduct() { return new HuaweiPhone(); }
@Override public IRouterProduct irouterProduct() { return new HuaweiRouter(); } }
|
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 UML.factory.abstract1;
public class Client { public static void main(String[] args) {
System.out.println("=============小米系列产品============="); XiaomiFactory xiaomiFactory = new XiaomiFactory();
IphoneProduct iphoneProduct = xiaomiFactory.iphoneProduct(); iphoneProduct.callup(); iphoneProduct.sendSMS();
IRouterProduct xiaomiProduct = xiaomiFactory.irouterProduct(); xiaomiProduct.openwifi();
System.out.println("=============华为系列产品============="); HuaweiFactory huaweiFactory = new HuaweiFactory();
IphoneProduct iphoneProduct1 = huaweiFactory.iphoneProduct(); iphoneProduct1.callup(); iphoneProduct1.sendSMS();
IRouterProduct huaweiProduct = huaweiFactory.irouterProduct(); huaweiProduct.openwifi(); } }
|
适配器模式
结构型模式
作用:
- 从程序结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题
分类:
- 适配器模式
- 代理模式
- 桥接模式
- 装饰模式
- 组合模式
- 外观模式
- 享元模式
适配器模式
类比:
1 2 3 4 5 6 7 8 9
| package UML.adapter;
public class Adaptee {
public void request(){ System.out.println("连接网线上网"); } }
|
1 2 3 4 5 6 7 8 9
| package UML.adapter;
public interface Net2Usb {
public void handleRequest(); }
|
1 2 3 4 5 6 7 8 9 10 11
| package UML.adapter;
public class Adapter extends Adaptee implements Net2Usb{ @Override public void handleRequest() { super.request(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| package UML.adapter;
public class Adapter2 implements Net2Usb{ private Adaptee adaptee;
public Adapter2(Adaptee adaptee) { this.adaptee = adaptee; }
@Override public void handleRequest() { adaptee.request(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package UML.adapter;
public class Computer {
public void net(Net2Usb adapter){ adapter.handleRequest(); }
public static void main(String[] args) { Computer computer = new Computer(); Adaptee adaptee = new Adaptee(); Adapter adapter = new Adapter(); Adapter2 adapter2 = new Adapter2(adaptee);
computer.net(adapter); computer.net(adapter2); } }
|
将一个类的接口转换为客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作!
角色分析:
- 目标接口:客户所期待的接口,目标可以是具体的或抽象的类,也可以是接口
- 需要适配的类:需要适配的类或适配者类
- 适配器:通过包装一个需要适配的对象,把原接口转换成目标对象!
对象适配器优点:
- 一个对象适配器可以把多个不同的适配者适配到同一个目标
- 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”适配者的子类也可以通过该适配器进行适配
类适配器的缺点:
- 对于Java、C#等不支持多继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
- 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性
适用场景:
- 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码
- 想创建一个可以重复使用的类,用于一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
桥接模式
桥接模式bridge
桥接模式是将抽象部分与实现部分分离,使他们都可以独立地变化。它是一种对象结构模式,又称为柄体(Handle and Body)模式或接口(Interface)模式
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
| package UML.bridge;
public interface Brand { void info(); }
class Apple implements Brand{ @Override public void info() { System.out.print("苹果"); } }
class Lenovo implements Brand{ @Override public void info() { System.out.print("联想"); } }
class Dell implements Brand{ @Override public void info() { System.out.print("戴尔"); } }
|
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
| package UML.bridge;
public abstract class Computer {
private Brand brand;
public Computer(Brand brand) { this.brand = brand; }
public void info(){ brand.info(); } }
class Desktop extends Computer{
public Desktop(Brand brand) { super(brand); }
@Override public void info() { super.info(); System.out.println("台式机"); } }
class Laptop extends Computer{
public Laptop(Brand brand) { super(brand); }
@Override public void info() { super.info(); System.out.println("笔记本"); } }
class Tablet extends Computer{
public Tablet(Brand brand) { super(brand); }
@Override public void info() { super.info(); System.out.println("平板电脑"); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| package UML.bridge;
public class Test { public static void main(String[] args) { Computer computer1 = new Laptop(new Apple()); computer1.info(); Computer computer2 = new Desktop(new Lenovo()); computer2.info(); } }
|
好处分析:
- 桥接模式偶尔类似于多继承方案,但多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大地减少了子类的个数,从而降低管理和维护的成本。
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!
劣势分析:
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者对抽象进行设计和编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。(耦合性过强时无法使用)
最佳实践:
- 如果一个系统需要在构建抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。抽象化角色和实现化角色可以以继承的方式独立扩展互不影响,在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合、
- 一个类存在两个独立变化的维度,且这两个维度都需要扩展
- 虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。