设计模式-简单篇
了解设计模式
- 设计模式 是前辈们对代码开发经验的总结,是解决特定问题的一系列套路,他不是语法规定, 而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。
- 1995年,GoF合作出版了《设计模式:可复用面向对象软件的基础》一书,共收录了23种设计模式,从此树立了软件设计模式领域的里程碑,人称GoF设计模式。
项目代码地址:https://gitee.com/zwtgit/design-patterns
学习设计模式的意义
- 设计模式的本质是面向对象设计原则的实际运用,是对类的封装性, 继承性和多态性以及类的关联关系和组合关系的充分理解
- 正确使用设计模式具有以下优点:
- 可以提高程序员的思维能力, 编程能力和设计能力
- 使程序设计更加标准化, 代码编制更加工程化, 使软件开发效率大大提高, 从而缩短软件的开发周期
- 使设计的代码可重用性高, 可读性强, 可靠性高, 灵活性好, 可维护性强
GoF 23(分类)
- 创建型模式:
- 单例模式,工厂模式, 抽象工厂模式, 建造者模式, 原型模式
- 结构型模式:
- 适配器模式, 桥接模式, 装饰模式, 组合模式, 外观模式, 享元模式, 代理模式
- 行为型模式:
- 模板方法模式, 命令模式, 迭代器模式, 观察者模式, 中介者模式, 备忘录模式, 解释器模式, 状态模式, 策略模式, 职责链模式, 访问者模式
OOP 七大原则
开闭原则:对扩展开放, 对修改关闭
里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立
依赖倒置原则:要面向接口编程, 不要面向实现编程
单一职责原则:控制类的粒度大小, 将对象解耦, 提高其内聚性
接口隔离原则:要为各个类建立它们需要的专用接口
迪米特法则:只与你的直接朋友交谈,不跟"陌生人"说话
合成复用原则:尽量先使用组合或者聚合等关联关系实现,其次才考虑使用继承关系来实现
单例模式
可以看我之前的博客:https://www.cnblogs.com/zwtblog/p/15129821.html#单例模式
核心作用:
- 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点
常见场景:
- Windows的任务管理器
- Windows的回收站
- 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
- 网站的计数器一般也会采用单例模式,可以保证同步
- 数据库连接池的设计一 般也是单例模式
- 在Servlet编程中, 每个Servlet也是单例的
- 在Spring中, 每个Bean默认就是单例的
工厂模式
作用:
- 实现了创建者和调用者的分离
- 详细分类:
- 简单工厂模式
- 工厂方法模式
- 抽象工厂模式
OOP七大原则
- 开闭原则: r个软件的实体应当对扩展开放,对修改关闭
- 依赖倒转原则:要针对接口编程,不要针对实现编程
- 迪米特法则:只与你直接的朋友通信,而避免和陌生人通信
核心本质
- 实例化对象不使用new, 用工厂方法代替
- 将选择实现类, 创建对象统一管理和控制. 从而将调用者跟我们的实现类解耦
三种模式:
- 简单工厂模式
- 用来生产统一等级结构中的任意产品(对于增加新的产品,需要覆盖已有代码)
- 工厂方法模式
- 用来生产同一等级结构中的固定产品(支持增加任意产品)
- 抽象工厂模式
- 围绕一个超级工厂创建其他工厂, 该超级工厂又称为其他工厂的工厂
简单工厂模式
package factory.simple;
public interface Car {
void name();
}
package factory.simple;
public class WuLing implements Car{
@Override
public void name() {
System.out.println("我是五菱宏光");
}
}
package factory.simple;
public class Tesla implements Car{
@Override
public void name() {
System.out.println("我是特斯拉");
}
}
package factory.simple;
public class Consumer {
public static void main(String[] args) {
// WuLing wuLing = new WuLing();
// Tesla tesla = new Tesla();
// wuLing.name();
// tesla.name();
//使用工厂
Car car = CarFactory.getCar("特斯拉");
car.name();
}
}
package 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;
}
}
}
工厂方法模式
package factory.method;
//工厂方法模式
public interface CarFactory {
Car getCar();
}
package factory.method;
public class Tesla implements Car {
@Override
public void name() {
System.out.println("我是特斯拉");
}
}
package factory.method;
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
package factory.method;
public class Consumer {
public static void main(String[] args) {
Car car = new WuLingFactory().getCar();
car.name();
}
}
抽象工厂模式
定义:抽象工厂模式提供了一个创建一系列相关或者相互依赖对象的接口,无需指定它们具体的类
适用场景:
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量的重复代码
提供一个产品类的库,所有的产品以同样的接口出现,从而使得客户端不依赖于具体的实现
优点:
- 具体产品在应用层的代码隔离,无需关心创建的细节
- 将一个系列的产品统一到一起创建
缺点:
- 规定了所有可能被创建的产品集合,产品簇中扩展新的产品困难;
- 增加了系统的抽象性和理解难度
package factory.abstract1;
//手机产品接口
public interface IPhoneProduct {
void start();
void shutdown();
void call();
void sendMsg();
}
package factory.abstract1;
//路由器产品接口
public interface IRouterProduct {
void start();
void shutdown();
void openWifi();
void setting();
}
package factory.abstract1;
//抽象产品工厂
public interface IProductFactory {
//生产手机
IPhoneProduct iPhoneProduct();
//生产路由器
IRouterProduct iRouterProduct();
}
package factory.abstract1;
public class XiaomiFactory implements IProductFactory {
@Override
public IPhoneProduct iPhoneProduct() {
return new XiaomiPhone();
}
@Override
public IRouterProduct iRouterProduct() {
return new XiaomiRouter();
}
}
package factory.abstract1;
public class XiaomiPhone implements IPhoneProduct {
@Override
public void start() {
System.out.println("小米开机");
}
@Override
public void shutdown() {
System.out.println("小米关机");
}
@Override
public void call() {
System.out.println("小米打电话");
}
@Override
public void sendMsg() {
System.out.println("小米发信息");
}
}
package factory.abstract1;
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("小米设置路由器");
}
}
package factory.abstract1;
public class Client {
public static void main(String[] args) {
System.out.println("=================小米系列产品==============");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
IPhoneProduct iPhoneProduct = xiaomiFactory.iPhoneProduct();
iPhoneProduct.start();
IRouterProduct iRouterProduct = xiaomiFactory.iRouterProduct();
iRouterProduct.start();
}
}
建造者模式
建造者模式也属于创建型模式,它提供了一种创建对象的最佳方式。
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示
主要作用:在用户不知道对象的建造过程和细节的情况下就可以直接创建复杂的对象。
用户只需要给出指定复杂对象的类型和内容,建造者模式负责按顺序创建复杂对象(把内部的建造过程和细节隐藏起来)
例子:
工厂(建造者模式):负责制造汽车(组装过 > 程和细节在工厂内)
汽车购买者(用户)∶你只需要说出你需要的 > 型号(对象的类型和内容),然后直接购买就可以使用了(不需要知道汽车是怎么组装的(车轮、车门、发动机、方向盘等等)
package builder.demo01;
//抽象的建造者:方法
public abstract class Builder {
abstract void builderA();//地基
abstract void builderB();//钢筋工程
abstract void builderC();//铺电线
abstract void builderD();//粉刷
//完工:得到产品
abstract Product getProduct();
}
package builder.demo01;
//产品,房子
public class Product {
private String builderA;
private String builderB;
private String builderC;
private String builderD;
public String getBuilderA() {
return builderA;
}
public void setBuilderA(String builderA) {
this.builderA = builderA;
}
public String getBuilderB() {
return builderB;
}
public void setBuilderB(String builderB) {
this.builderB = builderB;
}
public String getBuilderC() {
return builderC;
}
public void setBuilderC(String builderC) {
this.builderC = builderC;
}
public String getBuilderD() {
return builderD;
}
public void setBuilderD(String builderD) {
this.builderD = builderD;
}
@Override
public String toString() {
return "Product{" +
"builderA='" + builderA + '\'' +
", builderB='" + builderB + '\'' +
", builderC='" + builderC + '\'' +
", builderD='" + builderD + '\'' +
'}';
}
}
package builder.demo01;
//具体的建造者:工人
public class Worker extends Builder {
private Product product;
public Worker() {
product = new Product();
}
@Override
void builderA() {
product.setBuilderA("地基");
System.out.println("地基");
}
@Override
void builderB() {
product.setBuilderB("钢筋工程");
System.out.println("钢筋工程");
}
@Override
void builderC() {
product.setBuilderC("铺电线");
System.out.println("铺电线");
}
@Override
void builderD() {
product.setBuilderD("粉刷");
System.out.println("粉刷");
}
@Override
Product getProduct() {
return product;
}
}
package builder.demo01;
//指挥:核心,负责指挥构建一个工程,工程如何构建,由它决定
public class Director {
//指挥工人按照顺序建房子
public Product build(Builder builder) {
builder.builderA();
builder.builderB();
builder.builderC();
builder.builderD();
return builder.getProduct();
}
}
package builder.demo01;
public class Test {
public static void main(String[] args) {
//指挥
Director director = new Director();
//指挥具体的工人完成产品
Product build = director.build(new Worker());
System.out.println(build.toString());
}
}
package builder.demo02;
//建造者
public abstract class Builder {
abstract Builder BuildA(String msg);//"汉堡";
abstract Builder BuildB(String msg);//"可乐";
abstract Builder BuildC(String msg);//"薯条";
abstract Builder BuildD(String msg);//"甜点";
abstract Product getProduct();
}
package builder.demo02;
//产品:套餐
public class Product {
private String BuildA = "汉堡";
private String BuildB = "可乐";
private String BuildC = "薯条";
private String BuildD = "甜点";
public String getBuildA() {
return BuildA;
}
public void setBuildA(String buildA) {
BuildA = buildA;
}
public String getBuildB() {
return BuildB;
}
public void setBuildB(String buildB) {
BuildB = buildB;
}
public String getBuildC() {
return BuildC;
}
public void setBuildC(String buildC) {
BuildC = buildC;
}
public String getBuildD() {
return BuildD;
}
public void setBuildD(String buildD) {
BuildD = buildD;
}
@Override
public String toString() {
return "Product{" +
"BuildA='" + BuildA + '\'' +
", BuildB='" + BuildB + '\'' +
", BuildC='" + BuildC + '\'' +
", BuildD='" + BuildD + '\'' +
'}';
}
}
package builder.demo02;
public class Worker extends Builder {
private Product product;
public Worker() {
product = new Product();
}
@Override
Builder BuildA(String msg) {
product.setBuildA(msg);
return this;
}
@Override
Builder BuildB(String msg) {
product.setBuildB(msg);
return this;
}
@Override
Builder BuildC(String msg) {
product.setBuildC(msg);
return this;
}
@Override
Builder BuildD(String msg) {
product.setBuildD(msg);
return this;
}
@Override
Product getProduct() {
return product;
}
}
package builder.demo02;
public class Test {
public static void main(String[] args) {
//服务员
Worker worker = new Worker();
//可以按默认走,也可以自由组合
Product product = worker.BuildA("全家桶").BuildB("雪碧").getProduct();
System.out.println(product.toString());
}
}
建造者模式的意图和适用场景(拓展)
- 模式意图
将一个复杂的构件与其表示相分离,使得同样的构件过程可以创建不同的表示
- 使用场景
- 需要生成的产品对象有复杂的内部结构
- 当一些基本部件不会变, 而其组合经常变化的时候
- 需要生成的对象内部属性本身相互依赖
建造者的参与者(拓展)
Builder
抽象建造者,给出一个抽象接口,以规范产品对象的各个组成成分的建造。
这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建
ConcreteBuilder
实现Builder接口, 针对不同的商业逻辑, 具体化复杂对象的各部分的创建,,即具体建造者
Director
指挥者, 调用具体建造者来创建复杂对象的各个部分, 在指导者不涉及具体产品的信息,,只负责保证对象各部分完整创建或者按某种顺序创建
Product
要创建的复杂对象, 即产品角色
优缺点
优点:
-
产品的建造和表示分离,实现了解耦, 使用建造者模式可以使客户端不必知道产品内部组成的细节。
-
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
-
具体的建造者类之间是相互独立的,这有利于系统的扩展。
增加新的具体建造者无需修改原有的类库的代码,符合 ” 开闭原则 “
缺点:
- 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似。如果产品之间的差异性很大,,则不适合使用建造者模式,因此其使用范围受到一定的限制。
- 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类实现这种变化,导致系统变得很庞大
原型模式
克隆
Prototype
Cloneable接口
clone()方法
package Prototype.demo01;
import java.util.Date;
/*
1.实现一个接口Cloneable
2.重写一个方法clone()
*/
//Video
public class Video implements Cloneable { //无良up 主,克隆别人的视频! .
private String name;
private Date createTime;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public Video() {
}
public Video(String name, Date createTime) {
this.name = name;
this.createTime = createTime;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
@Override
public String toString() {
return "Video{" +
"name='" + name + '\'' +
", createTime=" + createTime +
'}';
}
}
package Prototype.demo01;
import java.util.Date;
/*
客户端:克隆
*/
public class Bilibili {
public static void main(String[] args) throws CloneNotSupportedException {
//原型对象v1
Date date = new Date();
Video v1 = new Video("ML李嘉图", date);
System.out.println("v1=>" + v1);
System.out.println("v1=>hash" + v1.hashCode());
System.out.println("---------------------------------");
//v1克隆v2
//Video v2 = new Video( "ML李嘉图", date);
Video v2 = (Video) v1.clone();
System.out.println("v1=>" + v1);
System.out.println("v2=>" + v2);
System.out.println("v1=>hash" + v2.hashCode());
System.out.println("---------------------------------");
v2.setName("Clone");
System.out.println("v1=>" + v1);
System.out.println("v2=>" + v2);
}
}
现在的代码是浅克隆
再实现深克隆
适配器模式
结构性模式:
-
作用
从程序的结构上实现松耦合,从而可以扩大整体的类结构,用来解决更大的问题
适配器模式就像USB网线转换器
package Adapter;
//要被适配的类:网线
public class Adaptee {
public void request() {
System.out.println("连接网线上网");
}
}
package Adapter;
//按口转换器的抽象实现~
public interface NetToUsb {
//作用:处理请求,网线=>usb
public void handleRequest();
}
package Adapter;
//真正的适配器,需 要连接USB,连接网线~
public class Adapter2 implements NetToUsb {
//组合模式
private Adaptee adaptee;
public Adapter2(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void handleRequest() {
//上网的具体实现,找一个转接头
adaptee.request();//可以上网
}
}
package Adapter;
//客户端关: 想上:网,插不上:网线~
public class Computer {
//我们的电脑需要连接:转换器才可以:网
public void net(NetToUsb adapter) {
//上网的具体实现,找个转换器
adapter.handleRequest();
}
public static void main(String[] args) {
//电脑,适配器,网线~
Computer computer = new Computer(); //电脑
Adaptee adaptee = new Adaptee(); //网线
Adapter2 adapter = new Adapter2(adaptee); //转换器
computer.net(adapter);
}
}
角色分析
-
将一个类的接口转换成客户希望的另外一个接口.。
-
Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作
-
目标接口:客户所期待的接口,目标可以是具体的或抽象的类,可以是接口
-
需要适配的类:需要适配的类或适配者类
-
适配器:通过包装一个需要适配的对象,把原接口转换成目标对象
对象适配器优点
- 一个对象适配器可以把多个不同的适配者适配到同一个目标
- 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据"里氏代换原则",适配者的子类也可通过该适配器进行适配
类适配器缺点
- 对于Java,C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者
- 在Java, C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性
适用场景
- 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需求,甚至没有这些类的源代
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作
桥接模式
桥接模式是将抽象部分与它的实现部分分离,使他们都可以独立地变化,
它是一种对象结构型模式,又称为柄体(Handle and Body)模式或接口(Interface)模式
package bridge;
//品牌
public interface Brand {
void info();
}
package bridge;
//联想品牌
public class Lenovo implements Brand {
@Override
public void info() {
System.out.print("联想");
}
}
package bridge;
//联想品牌
public class Apple implements Brand {
@Override
public void info() {
System.out.print("苹果");
}
}
package bridge;
//抽象的电脑类型类
public abstract class Computer {
//组合,品牌~
protected 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.print("台式机");
}
}
class Laptop extends Computer {
public Laptop(Brand brand) {
super(brand);
}
@Override
public void info() {
super.info();
System.out.print("笔记本");
}
}
package bridge;
public class Test {
public static void main(String[] args) {
//苹果笔记本
Computer computer = new Laptop(new Apple());
computer.info();
System.out.println();
//联想台式机
Computer computer2 = new Desktop(new Lenovo());
computer2.info();
}
}
优劣势分析
好处分析:
- 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法。极大的减少了子类的个数,从而降低管理和维护的成本
- 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来!
劣势分析:
- 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程。
- 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性.
最佳实践:
如果一个系统需要在构建的抽象化角色和具体化角色之间增加更多的灵活性,避免在两个层次之间建立静态的继承联系,
通过桥接模式可以使它们在抽象层建立一个关联关系。
抽象化角色和实现化角色可以以继承的方式独立扩展而互不影响,
在程序运行时可以动态将一个抽象化子类的对象和一个实现化子类的对象进行组合,即系统需要对抽象化角色和实现化角色进行动态耦合。
一个类存在两个独立变化的维度,且这两个维度都需要进行扩展。
虽然在系统中使用继承是没有问题的,但是由于抽象化角色和具体化角色需要独立变化,设计要求需要独立管理这两者。
对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
场景:
- Java语言通过Java虚拟机实现了平台的无关性
- AWT中的Peer架构
- JDBC驱动程序也是桥接模式的应用之一
静态代理和动态代理
AOP的底层机制就是动态代理
详情可见:https://www.cnblogs.com/zwtblog/p/15063775.html#静态and动态代理