文章目录
前言
本篇是关于设计模式中建造者模式、适配器模式(3种)、以及桥接模式的笔记。
一、建造者模式
建造者模式是属于创建型设计模式
,通过一步步构建一个复杂对象的方式,隐藏复杂的构造过程使得对象的创建过程更加灵活、易于扩展,同时不需要指定具体的构造方式。
其组成部分有:
- 产品:表示被建造的对象。
- 抽象建造者:通常是抽象类或接口,定义了创建产品所需要的方法,由不同的子类去描述建造不同产品的
具体步骤
。 - 指挥者:组合了建造者对象,通常是将建造者对象作为其属性,提供方法控制
建造的过程
,指挥建造者完成特定的构建步骤。 - 具体建造者:继承/实现抽象建造者,重写其中的方法,描述不同产品的建造步骤。
为什么要使用建造者模式?我们可以看一个例子,假设现在需要建一所房子,用一个实体类封装房子的属性:
java
/**
* 不管是 高层,洋房,还是别墅 都有 固定的这些属性
*/
public class House {
private String basic;
private String wall;
private String roof;
public House() {
}
public House(String basic, String wall, String roof) {
this.basic = basic;
this.wall = wall;
this.roof = roof;
}
//....get set 方法
}
客户端直接完成对房子的创建,这样做很明显是存在弊端的。因为房子的种类有很多种,每一种的建造过程也是不一样的,虽然都包含一些共有的属性:
java
public class Client {
public static void main(String[] args) {
House house = new House();
house.setBasic("打地基");
house.setWall("砌墙");
house.setRoof("盖屋顶");
//--------如果有不同的房子要建立呢?---------------
// 伪代码
// if (type.equals("别墅")){
// house.setBasic("打别墅的地基");
// house.setWall("砌别墅的墙");
// house.setRoof("盖别墅的屋顶");
// }else {
// house.setBasic("打高层的地基");
// house.setWall("砌高层的墙");
// house.setRoof("盖高层的屋顶");
// }
//--------如果后续对于工序还有不同的要求呢?---------------
}
}
使用建造者模式改进:
- 引入一个房子的建造者,定义了建不同房子都必须有的共有的工序:
java
/**
* 房子的建造者
*/
public abstract class CommonHouseBuilder {
protected House house = new House();
abstract void buildBasic();
abstract void buildWalls();
abstract void buildRoof();
/**
* 返回建造好的房子
* @return
*/
public House getHouse() {
return house;
}
}
- 具体的产品实现类,代表建立不同类型的房子:
java
public class HighRise extends CommonHouseBuilder{
@Override
void buildBasic() {
System.out.println("高层地基");
}
@Override
void buildWalls() {
System.out.println("高层砌墙");
}
@Override
void buildRoof() {
System.out.println("高层封顶");
}
}
java
public class Villa extends CommonHouseBuilder{
@Override
void buildBasic() {
house.setBasic("别墅打基础");
}
@Override
void buildWalls() {
house.setWall("别墅砌墙");
}
@Override
void buildRoof() {
house.setRoof("别墅封顶");
}
}
- 引入指挥者,指定建造的顺序:
java
/**
* 建房子的指挥者
*/
public class HouseBuildCommander {
CommonHouseBuilder commonHouseBuilder;
public HouseBuildCommander(CommonHouseBuilder commonHouseBuilder) {
this.commonHouseBuilder = commonHouseBuilder;
}
public HouseBuildCommander() {
}
/**
* 建造房子
* @return
*/
public House constructHouse() {
commonHouseBuilder.buildBasic();
commonHouseBuilder.buildWalls();
commonHouseBuilder.buildRoof();
return commonHouseBuilder.getHouse();
}
}
- 客户端只需要让指挥者去指挥建造需要的房子即可:
java
public class Client {
public static void main(String[] args) {
//让指挥者 建造别墅
HouseBuildCommander houseBuildCommander = new HouseBuildCommander(new Villa());
House house = houseBuildCommander.constructHouse();
System.out.println(house);
}
}
House{basic = 别墅打基础, wall = 别墅砌墙, roof = 别墅封顶}
从改造后的案例,不难看出,建造者模式,一般适用于产品建造过程
有较多共同点的情况,例如上面的案例,无论是建别墅,高层也好,都需要经过打基础,砌墙,封顶的过程,只是不同的房子具体的工艺不一样。如果产品之间建造过程差异很大,比如建房子和建桥,那么就不适合使用建造者模式。并且建造者模式
,和抽象工厂模式
的区别在于,建造者模式注重创建产品的过程,而抽象工厂模式,强调的是通过提供一个统一的接口来创建这些产品系列,而不关心具体实现。
- 如果你需要创建一组相关的产品,使用抽象工厂模式;
- 如果你需要按步骤创建一个复杂对象,使用建造者模式。
二、适配器模式
适配器模式是一种结构型设计模式
,适用于一个类的接口转换成客户端希望的另一个接口。通过适配器模式,可以让原本因接口不兼容而无法一起工作的类协同工作。
也可以用生活中的一个案例说明,例如有些笔记本电脑是支持直接插入网线的:
而有一些则没有网线接口,只有usb插槽,则需要在网线和usb插槽之间使用一个转接器
:
通过转接器,便可以实现有线上网的方式。这里的转接器就是适配器
的体现(在网线和usb插槽之间作为缓冲层,对两者进行适配转换)。
由此可知,适配器模式的主要角色有:
- 目标接口:客户端希望使用的接口。
- 源接口:需要被适配的接口,通常是一个现有的类或库,但它的接口与客户端不兼容。
- 适配器:实现目标接口的类,它将源接口转化为目标接口。
如果体现在代码中,适配器模式分为类适配器
,对象适配器
,接口适配器
三种体现,其中类适配器
的适配器类,是使用继承被适配类,实现适配类的形式,不够灵活,所以不再记录。
2.1、对象适配器
目前笔记本电脑有一个usb,无法直接插上网线:
java
public class Usb {
public String outputUsb(){
String type = "usb接口";
System.out.println("向外提供"+type);
return type;
}
}
网线接口:
java
public interface NetworkCable {
String outputNetworkCable();
}
通过适配器,使网线可以通过usb接口正常工作:
java
public class Adapt implements NetworkCable{
private Usb usb;
public Adapt(Usb usb) {
this.usb = usb;
}
@Override
public String outputNetworkCable() {
String usb = this.usb.outputUsb();
System.out.println("将usb接口转接为网线接口");
return "网线接口";
}
}
java
public class Computer {
public void connectInternet(NetworkCable networkCable){
if (networkCable.outputNetworkCable().equals("网线接口")){
System.out.println("连接互联网");
}else {
System.out.println("不能有线上网!");
}
}
}
电脑的使用者,只需要在连接网络时,通过适配器连上电脑的usb接口,再将网线插到适配器上,即可实现有线上网:
java
public class Client {
public static void main(String[] args) {
Computer computer = new Computer();
computer.connectInternet(new Adapt(new Usb()));
}
}
2.2、接口适配器
接口适配器,通常运用在某个具体的实现类,只想实现接口中部分方法的情况:
接口中有五个方法:
java
public interface Inter {
void method1();
void method2();
void method3();
void method4();
void method5();
}
但是某个实现类只想实现其中的部分方法,可以引入一个抽象类
作为中间层,实现所有的方法:
java
public abstract class AbsClass implements Inter{
@Override
public void method1() {
}
@Override
public void method2() {
}
@Override
public void method3() {
}
@Override
public void method5() {
}
@Override
public void method4() {
}
}
具体的实现类只需要继承
该抽象类
即可自由选择需要实现的方法:
java
public class BusinessClass extends AbsClass{
@Override
public void method1() {
System.out.println("只用到了method1");
}
}
三、桥接模式
桥接模式是一种结构型设计模式
,引入桥接对象来解耦抽象部分和具体实现部分,使抽象部分与实现部分分离。
其主要角色有:
- 抽象类:定义了抽象的接口,并持有一个对实现类对象的引用。抽象类的实现通常依赖于实现类。
- 扩展抽象类:是抽象类的具体子类,提供了具体的功能实现。
- 实现类接口:定义了实现类的接口,通常是一个抽象类或者接口,提供了具体实现类的基本方法。
- 具体实现类:实现了实现类接口的具体类,负责实现具体的功能。
如果举一个生活中的案例,例如手机有不同的款式,比如翻盖式,折叠式,不同的式样又有不同的品牌。假设需要新增一个样式,那么需要在新的样式下加上所有的品牌。(反之也一样,在所有的品牌下都加上新样式)。
引入桥接模式解决这样的问题,则可以将品牌
设置为一个接口,并定义所有品牌
手机都具有的功能(开机,关机,打电话):
java
/**
* 品牌
* 所有品牌的手机都有开机关机,打电话的功能
*/
public interface Brand {
void open();
void close();
void call();
}
不同品牌的手机实现品牌接口,重写其中的方法:
java
public class VivoPhone implements Brand {
@Override
public void open() {
System.out.println("vivo 手机 开机");
}
@Override
public void close() {
System.out.println("vivo 手机 关机");
}
@Override
public void call() {
System.out.println("vivo 手机 打电话");
}
}
java
public class XiaomiPhone implements Brand{
@Override
public void open() {
System.out.println("小米手机 开机");
}
@Override
public void close() {
System.out.println("小米手机 关机");
}
@Override
public void call() {
System.out.println("小米手机 打电话");
}
}
将手机
设置为抽象类,组合了品牌
,同时引用了不同品牌共有的方法:
java
public abstract class Phone {
private Brand brand;
public Phone(Brand b) {
this.brand = b;
}
public void open(){
brand.open();
}
public void close(){
brand.close();
}
public void call(){
brand.call();
}
}
不同样式
的手机,只需要继承手机抽象类即可:
java
public class FoldedPhone extends Phone{
public FoldedPhone(Brand b) {
super(b);
}
@Override
public void open() {
super.open();
System.out.println("折叠式 手机开机");
}
@Override
public void close() {
super.close();
System.out.println("折叠式 手机关机");
}
@Override
public void call() {
super.call();
System.out.println("折叠式 手机打电话");
}
}
创建一个折叠式的小米手机:
java
public class Client {
public static void main(String[] args) {
FoldedPhone foldedPhone = new FoldedPhone(new XiaomiPhone());
foldedPhone.call();
foldedPhone.open();
foldedPhone.close();
}
}
小米手机 打电话
折叠式 手机打电话
小米手机 开机
折叠式 手机开机
小米手机 关机
折叠式 手机关机
如果后续需要增加品牌
或者样式
,只需要实现品牌
接口,或者继承手机
抽象类即可。从这个案例不难看出,当一个系统有多个维度
需要变化时,桥接模式可以把这些维度分开,避免创建大量的子类和多重继承