庚子鼠年 己卯月 辛亥日
描述
设计模式学习
技术博客:
随笔
建造者模式
使用场景
- 当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。
解决的问题
当一个类的构造函数参数超过4个,而且这些参数有些是可选的时,我们通常有两种办法来构建它的对象。 例如我们现在有如下一个类计算机类Computer
,其中cpu与ram是必填参数,而其他3个是可选参数,那么我们如何构造这个类的实例呢,通常有两种常用的方式:
-
第一:折叠构造函数模式(telescoping constructor pattern ),这个我们经常用,如下代码所示(写很多构造方法)
-
第二种:Javabean 模式(用set刚刚传值)
那么这两种方式有什么弊端呢? 第一种主要是使用及阅读不方便。你可以想象一下,当你要调用一个类的构造函数时,你首先要决定使用哪一个,然后里面又是一堆参数,如果这些参数的类型很多又都一样,你还要搞清楚这些参数的含义,很容易就传混了。。。那酸爽谁用谁知道。 第二种方式在构建过程中对象的状态容易发生变化,造成错误。因为那个类中的属性是分步设置的,所以就容易出错。
为了解决这两个痛点,builder模式就横空出世了。
如何实现
传统模式
- 建造者(Builder):为创建一个产品对象的各个部件指定抽象接口。
- 具体建造者(ConcreteBuilder):实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并 提供一个检索产品的接口。
- 指挥者(Director):指挥并构造一个使用Builder接口的对象。
- 产品(Product):表示被构造的复杂对象(具体的实现类)。ConcreteBuilder创建该产品的内部表示并定义它的装配过程,包含定义组成部件的类,包括将这些部件装配成最终产品的接口。
静态内部类(推荐)
- 在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。
- 在Computer中创建一个private的构造函数,参数为Builder类型
- 在Builder中创建一个
public
的构造函数,参数为Computer中必填的那些参数,cpu 和ram。 - 在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例
- 在Builder中创建一个
build()
方法,在其中构建Computer的实例并返回
public class Computer {
private String cpu;//必须
private String ram;//必须
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
private Computer(Builder builder){
this.cpu=builder.cpu;
this.ram=builder.ram;
this.usbCount=builder.usbCount;
this.keyboard=builder.keyboard;
this.display=builder.display;
}
public static class Builder{
private String cpu;//必须
private String ram;//必须
private int usbCount;//可选
private String keyboard;//可选
private String display;//可选
// 这两个是必须的,所以写在构造函数中
// 提供public方法
public Builder(String cup,String ram){
this.cpu=cup;
this.ram=ram;
}
public Builder setUsbCount(int usbCount) {
this.usbCount = usbCount;
return this;
}
public Builder setKeyboard(String keyboard) {
this.keyboard = keyboard;
return this;
}
public Builder setDisplay(String display) {
this.display = display;
return this;
}
// 提供public方法
public Computer build(){
return new Computer(this);
}
}
}
参考文献:https://zhuanlan.zhihu.com/p/58093669
适配器模式
-
适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。
-
在适配器模式中,我们通过增加一个新的适配器类来解决接口不兼容的问题,使得原本没有任何关系的类可以协同工作。
-
根据适配器类与适配者类的关系不同,适配器模式可分为对象适配器和类适配器两种,在对象适配器模式中,适配器与适配者之间是关联关系;在类适配器模式中,适配器与适配者之间是继承(或实现)关系。
-
角色
Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
类适配器
// 被适配者的方法
public class Adaptee {
public void adapteeRequest() {
System.out.println("被适配者的方法");
}
}
// 适配器接口
public interface Target {
void request();
}
// 适配器
public class Adapter extends Adaptee implements Target{
@Override
public void request() {
// ...一些操作...
super.adapteeRequest();
// ...一些操作...
}
}
对象适配器
// 适配器
public class Adapter implements Target{
private Adaptee adaptee;
public Target(Adaptee adaptee){
this.adaptee = adaptee;
}
@Override
public void request() {
// ...一些操作...
adaptee.adapteeRequest();
// ...一些操作...
}
}
接口适配器
//目标接口,有多个方法
public interface IDCOutput {
public int output5V();
public int output12V();
public int output20V();
}
//中间类,空实现所有方法,这是一个抽象类
public abstract class DefaultAdapter implements IDCOutput {
@Override
public int output5V() {
return 0;
}
@Override
public int output12V() {
return 0;
}
@Override
public int output20V() {
return 0;
}
}
//我的mac电源适配器只需要实现20V的方法即可
public class MacAdatper extends DefaultAdapter {
private AC ac;
public MacAdatper(AC ac){
this.ac = ac;
}
@Override
public int output20V() {
return ac.outputAC()/11;
}
public static void main(String[] args) {
MacAdatper adatper = new MacAdatper(new AC());
System.out.println("mac电脑电压:" + adatper.output20V());
}
}
//输出结果:
//mac电脑电压:20
适配器模式总结
主要优点:
将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。
具体来说,类适配器模式还有如下优点:
由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还有如下优点:一个对象适配器可以把多个不同的适配者适配到同一个目标;
可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏代换原则”,适配者的子类也可通过该适配器进行适配。
类适配器模式的缺点如下:
- 对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者;
- 适配者类不能为最终类,如在Java中不能为final类,C#中不能为sealed类;
- 在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
对象适配器模式的缺点如下:
- 与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。
适用场景:
- 系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系统的需要,甚至没有这些类的源代码。
- 想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
参考博客:https://blog.csdn.net/wwwdc1012/article/details/82780560