23种设计模式学习笔记(1)
https://blog.csdn.net/qq_51495235/article/details/115358623
23种设计模式学习笔记(2)
https://blog.csdn.net/qq_51495235/article/details/115358846
9.1 工厂方法模式VS建造者模式
工厂方法模式注重的是整体对象的创建方式;而建造者模式注重的是部件构建的过程,意在通过一步一步地精确构造创建出一个复杂的对象。
我们举个简单例子来说明两者的差异,如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷、能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、头、脚、躯干等部分,然后再把内裤外穿,于是一个超人就诞生了。
9.2 抽象工厂模式VS建造者模式
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式则是不需要关心构建过程,只关心什么产品由什么工厂生产即可。
建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。
- 建造者模式更加注重方法的调用顺序,工厂模式注重创建对象。
- 创建对象的力度不同,建造者模式创建复杂的对象,由各种复杂的部件组成,工厂模式创建出来的对象都一样
- 关注重点不一样,工厂模式只需要把对象创建出来就可以了,而建造者模式不仅要创建出对象,还要知道对象由哪些部件组成。
- 建造者模式根据建造过程中的顺序不一样,最终对象部件组成也不一样。
10,适配器模式
10.1 适配器模式的定义和特点
适配器模式(Adapter)的定义如下:**将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。**适配器模式分为类结构型模式和对象结构型模式两种,前者类之间的耦合度比后者高。
该模式的主要优点如下:
- 客户端通过适配器可以透明地调用目标接口。
- 复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
- 将目标类和适配者类解耦,解决了目标类和适配者类接口不一致的问题。
- 在很多业务场景中符合开闭原则。
其缺点是:
- 适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
10.2 适配器模式的结构与实现
10.2.1 适配器模式的结构
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
10.2.2 代码实现
10.2.2.1类适配器模式
Voltage5V 目标接口
package com.zhuang.adapter.classadapter;
/**
* @Classname Voltage5V
* @Description 定义直流电
* @Date 2021/3/21 14:14
* @Created by dell
*/
public interface Voltage5V {
//定义一个标准充电器来实现
public int output5V();
}
Voltage220V
package com.zhuang.adapter.classadapter;
/**
* @Classname Voltage220V
* @Description 创建交流电
* @Date 2021/3/21 14:13
* @Created by dell
*/
public class Voltage220V {
public int output220V() {
System.out.println("voltage 输出220伏");
return 220;
}
}
VoltageAdapter
package com.zhuang.adapter.classadapter;
/**
* @Classname VoltageAdapter
* @Description 创建充电器
* @Date 2021/3/21 14:14
* @Created by dell
*/
public class VoltageAdapter extends Voltage220V implements Voltage5V {
@Override
public int output5V() {
//获取交流电220V
int output220V = output220V();
//转为5V
int output5V = output220V / 44;
System.out.println("VoltageAdapter 输出5伏");
return output5V;
}
}
Phone
package com.zhuang.adapter.classadapter;
/**
* @Classname Phone
* @Description 手机类
* @Date 2021/3/21 14:15
* @Created by dell
*/
public class Phone {
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("电压5伏,可以充电");
} else if (voltage5V.output5V() > 5) {
System.out.println("电压过大,不可以充电");
}
}
}
Client
package com.zhuang.adapter.classadapter;
/**
* @Classname Client
* @Description 客户端类
* @Date 2021/3/21 14:15
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
System.out.println("==类适配器==");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
类适配器模式注意事项和细节
- Java是单继承机制,所以类适配器需要继承适配者(Adaptee,指Voltage220V)类,这点算是一个缺点,除此之外还必须要求目标(Target,指Voltage5V)必须是接口,有一定局限性;
- 适配者Voltage220V类的方法在适配器VoltageAdapter类中都会暴露出来,也增加了使用的成本。但是由于其继承了适配者Voltage220V类,所以它可以根据需求重写该类的方法,使得适配器VoltageAdapter类的灵活性增强了。
10.2.2.2 对象适配器模式
Voltage5V
package com.zhuang.adapter.objectadapter;
/**
* @Classname Voltage5V
* @Description 充电5V
* @Date 2021/3/21 14:32
* @Created by dell
*/
public interface Voltage5V {
//定义一个标准充电器来实现
public int output5V();
}
Voltage220V
package com.zhuang.adapter.objectadapter;
/**
* @Classname Voltage220V
* @Description 输出220V类
* @Date 2021/3/21 14:32
* @Created by dell
*/
public class Voltage220V {
public int output220V() {
System.out.println("voltage 输出220伏");
return 220;
}
}
VoltageAdapter
package com.zhuang.adapter.objectadapter;
/**
* @Classname VoltageAdapter
* @Description 适配器类
* @Date 2021/3/21 14:33
* @Created by dell
*/
public class VoltageAdapter implements Voltage5V {
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
//获取交流电220V
int output220V = voltage220V.output220V();
//转为5V
int output5V = output220V / 44;
System.out.println("VoltageAdapter 输出5伏");
return output5V;
}
}
Phone
package com.zhuang.adapter.objectadapter;
/**
* @Classname Phone
* @Description 手机类
* @Date 2021/3/21 14:33
* @Created by dell
*/
public class Phone {
public void charging(Voltage5V voltage5V) {
if (voltage5V.output5V() == 5) {
System.out.println("电压5伏,可以充电");
} else if (voltage5V.output5V() > 5) {
System.out.println("电压过大,不可以充电");
}
}
}
Client
package com.zhuang.adapter.objectadapter;
/**
* @Classname Client
* @Description 对象适配器测试类
* @Date 2021/3/21 14:33
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
System.out.println("==对象适配器==");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
对象适配器模式注意事项和细节
-
对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。
-
根据合成复用原则,使用组合替代继承, 所以它解决了类适配器中VoltageAdapter必须继承Voltage220V的局限性问题,也不再强制要求Voltage5V必须是接口。使用成本更低,更灵活。因此,对象适配器模式是适配器模式常用的一种。
10.2.2.3 接口适配器模式
Animation
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname Animation
* @Description 动画接口
* @Date 2021/3/21 14:47
* @Created by dell
*/
public interface Animation {
public void method1();
public void method2();
public void method3();
public void method4();
public void method5();
}
AnimationAdapter
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname AnimationAdapter
* @Description 接口适配器类
* @Date 2021/3/21 14:48
* @Created by dell
*/
public class AnimationAdapter implements Animation {
//全部都空实现
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method4() {
}
@Override
public void method5() {
}
}
JFrameAnimation
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname JFrameAnimation
* @Description 适配器子类
* @Date 2021/3/21 14:49
* @Created by dell
*/
public class JFrameAnimation extends AnimationAdapter {
@Override
public void method1() {
System.out.println("method1()被调用了...");
}
@Override
public void method2() {
System.out.println("method2()被调用了...");
}
}
Client
package com.zhuang.adapter.interfaceadapter;
/**
* @Classname Client
* @Description 客户端类
* @Date 2021/3/21 14:50
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
JFrameAnimation animation = new JFrameAnimation();
animation.method1();
animation.method2();
}
}
10.3 SpringMVC源码解析
Controller
package com.zhuang.adapter.springmvc;
/**
* @Classname Controller
* @Description springmvc的Controller源码
* @Date 2021/3/21 14:53
* @Created by dell
*/
//多种Controller实现
public interface Controller {
}
class HttpController implements Controller {
public void doHttpHandler() {
System.out.println("http...");
}
}
class SimpleController implements Controller {
public void doSimplerHandler() {
System.out.println("simple...");
}
}
class AnnotationController implements Controller {
public void doAnnotationHandler() {
System.out.println("annotation...");
}
}
DispatchServlet
package com.zhuang.adapter.springmvc;
import java.util.ArrayList;
import java.util.List;
/**
* @Classname DispatchServlet
* @Description springmvc的DispatchServlet源码
* @Date 2021/3/21 14:52
* @Created by dell
*/
public class DispatchServlet {
public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();
public DispatchServlet() {
handlerAdapters.add(new AnnotationHandlerAdapter());
handlerAdapters.add(new HttpHandlerAdapter());
handlerAdapters.add(new SimpleHandlerAdapter());
}
public void doDispatch() {
// 此处模拟SpringMVC从request取handler的对象,
// 适配器可以获取到希望的Controller
HttpController controller = new HttpController();
// AnnotationController controller = new AnnotationController();
//SimpleController controller = new SimpleController();
// 得到对应适配器
HandlerAdapter adapter = getHandler(controller);
// 通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
public HandlerAdapter getHandler(Controller controller) {
//遍历:根据得到的controller(handler), 返回对应适配器
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}
HandlerAdapter
package com.zhuang.adapter.springmvc;
/**
* @Classname HandlerAdapter
* @Description springmvc的HandlerAdapter源码
* @Date 2021/3/21 14:53
* @Created by dell
*/
///定义一个Adapter接口
public interface HandlerAdapter {
public boolean supports(Object handler);
public void handle(Object handler);
}
// 多种适配器类
class SimpleHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((SimpleController) handler).doSimplerHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
}
class HttpHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((HttpController) handler).doHttpHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
}
class AnnotationHandlerAdapter implements HandlerAdapter {
@Override
public void handle(Object handler) {
((AnnotationController) handler).doAnnotationHandler();
}
@Override
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
}
11,桥接模式
11.1 桥接模式的定义和特点
桥接(Bridge)模式的定义如下:**将抽象与实现分离,使它们可以独立变化。**它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
通过上面的讲解,我们能很好的感觉到桥接模式遵循了里氏替换原则和依赖倒置原则,最终实现了开闭原则,对修改关闭,对扩展开放。
桥接(Bridge)模式的优点是:
- 抽象与实现分离,扩展能力强
- 符合开闭原则
- 符合合成复用原则
- 其实现细节对客户透明
缺点是:
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,能正确地识别出系统中两个独立变化的维度,这增加了系统的理解与设计难度。
11.2 桥接模式的结构与实现
11.2.1 桥接模式的结构
- 抽象化(Abstraction)角色:定义抽象类,并包含一个对实现化对象的引用。
- 扩展抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
- 实现化(Implementor)角色:定义实现化角色的接口,供扩展抽象化角色调用。
- 具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5A1eyx2-1617191697077)(C:\Users\dell\Desktop\Notes\设计模式\img\Snipaste_2021-03-30_20-43-27.png)]
11.2.2 代码实现
Brand 抽象化角色
package com.zhuang.bridge;
/**
* @Classname Brand
* @Description 品牌类
* @Date 2021/3/22 9:31
* @Created by dell
*/
public interface Brand {
void open();
void call();
void close();
}
Vivo 拓展抽象化角色
package com.zhuang.bridge;
/**
* @Classname Vivo
* @Description 手机品牌 实现品牌接口
* @Date 2021/3/22 9:30
* @Created by dell
*/
public class Vivo implements Brand {
@Override
public void open() {
System.out.println("Vivo手机开机");
}
@Override
public void call() {
System.out.println("Vivo手机打电话");
}
@Override
public void close() {
System.out.println("Vivo手机关机");
}
}
XiaoMi 拓展抽象化角色
package com.zhuang.bridge;
/**
* @Classname XiaoMi
* @Description 手机品牌 实现品牌接口
* @Date 2021/3/22 9:30
* @Created by dell
*/
public class XiaoMi implements Brand {
@Override
public void open() {
System.out.println("XiaoMi手机开机");
}
@Override
public void call() {
System.out.println("XiaoMi手机打电话");
}
@Override
public void close() {
System.out.println("XiaoMi手机关机");
}
}
Phone
package com.zhuang.bridge;
/**
* @Classname Phone
* @Description 手机类 抽象类
* @Date 2021/3/22 9:30
* @Created by dell
*/
public abstract class Phone {
//组合品牌
private Brand brand;
//构造器
public Phone(Brand brand) {
super();
this.brand = brand;
}
protected void open() {
this.brand.open();
}
protected void close() {
this.brand.close();
}
protected void call() {
this.brand.call();
}
}
FoldedPhone
package com.zhuang.bridge;
/**
* @Classname FoldedPhone
* @Description 折叠手机类
* @Date 2021/3/22 9:31
* @Created by dell
*/
public class FoldedPhone extends Phone {
//构造器
public FoldedPhone(Brand brand) {
super(brand);
}
@Override
public void open() {
super.open();
System.out.println("折叠手机样式");
}
@Override
public void call() {
super.call();
System.out.println("折叠手机样式");
}
@Override
public void close() {
super.close();
System.out.println("折叠手机样式");
}
}
UpRightPhone
package com.zhuang.bridge;
/**
* @Classname UpRightPhone
* @Description 直立手机类
* @Date 2021/3/22 9:33
* @Created by dell
*/
public class UpRightPhone extends Phone {
//构造器
public UpRightPhone(Brand brand) {
super(brand);
}
@Override
public void open() {
super.open();
System.out.println("直立手机样式");
}
@Override
public void call() {
super.call();
System.out.println("直立手机样式");
}
@Override
public void close() {
super.close();
System.out.println("直立手机样式");
}
}
Client
package com.zhuang.bridge;
/**
* @Classname Client
* @Description 客户端类
* @Date 2021/3/22 9:30
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
Phone phone1 = new FoldedPhone(new XiaoMi());
phone1.open();
phone1.call();
phone1.close();
System.out.println("==============================");
Phone phone2 = new UpRightPhone(new Vivo());
phone2.open();
phone2.call();
phone2.close();
}
}
11.3 桥接模式的应用场景
当一个类内部具备两种或多种变化维度时,使用桥接模式可以解耦这些变化的维度,使高层代码架构稳定。
- 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
- 当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
- 当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。
12,装饰器模式
12.1 装饰器模式的定义和特点
装饰器(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。
装饰器模式的主要优点有:
- 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
- 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
- 装饰器模式完全遵守开闭原则
其主要缺点是:
- 装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
12.2 装饰器模式的结构与实现
通常情况下,扩展一个类的功能会使用继承方式来实现。但继承具有静态特征,耦合度高,并且随着扩展功能的增多,子类会很膨胀。如果使用组合关系来创建一个包装对象(即装饰对象)来包裹真实对象,并在保持真实对象的类结构不变的前提下,为其提供额外的功能,这就是装饰器模式的目标
12.2.1 装饰器模式的结构
- 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
12.2.2 代码实现
关系类图
FastFood 抽象构件角色
package com.zhuang.decorator;
/**
* @Classname FastFood
* @Description 快餐类 接口
* @Date 2021/3/23 21:54
* @Created by dell
*/
public abstract class FastFood {
private float price;
private String desc; //描述
public FastFood() {
}
public FastFood(float price, String desc) {
this.price = price;
this.desc = desc;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
//抽象方法 获取价格
public abstract float cost();
}
FiredRice 具体构建角色
package com.zhuang.decorator;
/**
* @Classname FiredRice
* @Description 炒饭类 继承快餐类
* @Date 2021/3/23 21:54
* @Created by dell
*/
public class FiredRice extends FastFood {
public FiredRice() {
super(10, "炒饭");
}
@Override
public float cost() {
return getPrice();
}
}
FiredNoodles 具体构建角色
package com.zhuang.decorator;
/**
* @Classname FiredNoodles
* @Description 炒面类 继承 快餐类
* @Date 2021/3/23 21:54
* @Created by dell
*/
public class FiredNoodles extends FastFood {
public FiredNoodles() {
super(15, "炒面");
}
@Override
public float cost() {
return getPrice();
}
}
Garnish 抽象装饰角色
package com.zhuang.decorator;
/**
* @Classname Garnish
* @Description 抽象配料类 继承快餐类
* @Date 2021/3/23 21:58
* @Created by dell
*/
public abstract class Garnish extends FastFood{
private FastFood fastFood;
public FastFood getFastFood() {
return fastFood;
}
public void setFastFood(FastFood fastFood) {
this.fastFood = fastFood;
}
public Garnish(FastFood fastFood,float price,String desc){
super(price,desc);
this.fastFood = fastFood;
}
}
Egg 具体装饰角色
package com.zhuang.decorator;
/**
* @Classname Egg
* @Description 鸡蛋配料类 继承配料类
* @Date 2021/3/23 21:55
* @Created by dell
*/
public class Egg extends Garnish {
public Egg(FastFood fastFood) {
//鸡蛋1元
super(fastFood, 1, "鸡蛋");
}
@Override
public float cost() {
return getPrice() + getFastFood().getPrice();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
Bacon 具体装饰角色
package com.zhuang.decorator;
/**
* @Classname Bacon
* @Description 培根类 继承配料类
* @Date 2021/3/23 22:03
* @Created by dell
*/
public class Bacon extends Garnish {
public Bacon(FastFood fastFood) {
//培根2元
super(fastFood, 2, "培根");
}
@Override
public float cost() {
return getPrice() + getFastFood().getPrice();
}
@Override
public String getDesc() {
return super.getDesc() + getFastFood().getDesc();
}
}
Client
package com.zhuang.decorator;
/**
* @Classname Client
* @Description 装饰器模式测试类
* @Date 2021/3/23 21:53
* @Created by dell
*/
public class Client {
public static void main(String[] args) {
//点一份炒饭
FastFood rice = new FiredRice();
//价格
System.out.println(rice.getDesc() + "-->" + rice.cost() + "元");
System.out.println("=============================");
//点一份加鸡蛋的炒饭
FastFood eggRice = new FiredRice();
//加鸡蛋
eggRice = new Egg(eggRice);
System.out.println(eggRice.getDesc() + "-->" + eggRice.cost() + "元");
System.out.println("=============================");
//点一份加培根的炒面
FastFood baconNoodles = new FiredNoodles();
//加培根
baconNoodles = new Bacon(baconNoodles);
System.out.println(baconNoodles.getDesc() + "-->" + baconNoodles.cost() + "元");
}
}
12.3 装饰器模式的应用场景
-
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
不能采用继承的情况主要有两类:
- 第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
- 第二类是因为类定义不能继承(如final类)
-
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
-
当对象的功能要求可以动态地添加,也可以再动态地撤销时。
12.4 JDK源码解析
IO流中的包装类使用到了装饰者模式。BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter。
我们以BufferedWriter举例来说明,先看看如何使用BufferedWriter
public class Demo {
public static void main(String[] args) throws Exception{
//创建BufferedWriter对象
//创建FileWriter对象
FileWriter fw = new FileWriter("C:\\Users\\dell\\Desktop\\a.txt");
BufferedWriter bw = new BufferedWriter(fw);
//写数据
bw.write("hello Buffered");
bw.close();
}
}
结构
BufferedWriter使用装饰者模式对Writer子实现类进行了增强,添加了缓冲区,提高了写数据的效率。
静态代理和装饰者模式的区别:
- 相同点:
- 都要实现与目标类相同的业务接口
- 在两个类中都要声明目标对象
- 都可以在不修改目标类的前提下增强目标方法
- 不同点:
- 目的不同 装饰者是为了增强目标对象 静态代理是为了保护和隐藏目标对象
- 获取目标对象构建的地方不同 装饰者是由外界传递进来,可以通过构造方法传递 静态代理是在代理类内部创建,以此来隐藏目标对象
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/140739.html