前言
这一段时间看商城的代码,觉得有必要对Java的设计模式进行一些复习,2018-10-24HeadFirst设计模式是HeadFirst中详细介绍的12种模式,这里对看到一些其他的几种模式进行补充。
构造者模式(Builder)
适用场景
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式
类图
通过AbstractBuilder来定义接口,而通过具体的实现来builder,调用相应的接口来生成对象。
builder模式与工厂模式挺像,都是为了生成对象,工程模式生成多种对象(1维、2维),builder模式仅生成一种。
举例
当一个构造函数的参数过多,而且有些还是可选项时,我们在实现上有2种方式:
-
通过定义多个构造函数来实现,每个构造函数的参数不同。
public class Computer {
...
public Computer(String cpu, String ram) {
this(cpu, ram, 0);
}
public Computer(String cpu, String ram, int usbCount) {
this(cpu, ram, usbCount, "罗技键盘");
}
public Computer(String cpu, String ram, int usbCount, String keyboard) {
this(cpu, ram, usbCount, keyboard, "三星显示器");
}
public Computer(String cpu, String ram, int usbCount, String keyboard, String display) {
this.cpu = cpu;
this.ram = ram;
this.usbCount = usbCount;
this.keyboard = keyboard;
this.display = display;
}
}
-
定义最小的构造函数,其他用setting来实现。
public class Computer {
...
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getRam() {
return ram;
}
public void setRam(String ram) {
this.ram = ram;
}
public int getUsbCount() {
return usbCount;
}
...
}
第一种的方式不太优雅,第二种不能保证在构造时候是适合的,通过setting使数据容易发生变化。
处理方案:Builder模式
builder模式是将这个类对象的构建,交给专门的builder类来实现。通过调用builder的函数,来准备数据,最后调用build,来生成对象。
public class Computer {
private final String cpu;//必须
private final String ram;//必须
private final int usbCount;//可选
private final String keyboard;//可选
private final 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 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 Computer build(){
return new Computer(this);
}
}
//省略getter方法
}
使用时:
Computer computer=new Computer.Builder("因特尔","三星")
.setDisplay("三星24寸")
.setKeyboard("罗技")
.setUsbCount(2)
.build();
这里是将Builder作为了Computer的内部类,其实并不是必须的。
提供者模式(Provider)
Provider模式是工厂模式与策略模式的结合,即工厂生成对象,而如何生成对象则由策略来决定,这个策略就是provider。
public interface ILoggerProvider : IDisposable
{
ILogger CreateLogger(string categoryName);
}
public interface ILoggerFactory : IDisposable
{
ILogger CreateLogger(string categoryName);
void AddProvider(ILoggerProvider provider);
}
可以往工厂里面添加 Provider。也就是说工厂里面可能存在着许许多多的提供程序。而这些提供程序可能都将是最后工厂创建出结果的必要支撑。
桥接模式(Bridge)
参考
适用场景
如果一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。可以通过将变化维度抽象出各自的类,通过组合方式再结合起来,这样防止因为很多继承带来的问题。
类图
classDiagram
Abstraction <|-- RefinedAbstraction
Abstraction *-- Implementor:组合
Implementor <|-- ConcreteImplementorA
Implementor <|-- ConcreteImplementorB
Abstraction : -impl:Implementor
Abstraction : +Abstraction()
Abstraction : +operation():void
class RefinedAbstraction{
+RefinedAbstraction()
+otherOperation():void
}
class Implementor{
+operationImpl()
}
class ConcreteImplementorA{
+operationImpl()
}
class ConcreteImplementorB{
+operationImpl()
}
桥梁模式所涉及的角色有:
- 抽象化(Abstraction)角色:抽象化给出的定义,并保存一个对实现化对象的引用。
- 修正抽象化(RefinedAbstraction)角色:扩展抽象化角色,改变和修正父类对抽象化的定义。
- 实现化(Implementor)角色:这个角色给出实现化角色的接口,但不给出具体的实现。必须指出的是,这个接口不一定和抽象化角色的接口定义相同,实际上,这两个接口可以非常不一样。实现化角色应当只给出底层操作,而抽象化角色应当只给出基于底层操作的更高一层的操作。
- 具体实现化(ConcreteImplementor)角色:这个角色给出实现化角色接口的具体实现。
举例
如咖啡,在容量维度上大杯、中杯、小杯...,在添加物维度上有牛奶、糖、蜂蜜...
如果用继承来完成,以上就有3×3种咖啡。这样将容量维度变成自身(Abstraction),然后将添加物抽象成实现(Implementor),然后让Abstraction来组合Implementor。
//Abstraction
public abstract class Coffee {
protected ICoffeeAdditives additives;
public Coffee(ICoffeeAdditives additives){
this.additives=additives;
}
public abstract void orderCoffee(int count);
}
//RefinedAbstraction:为Abstraction增加新的功能,应对变化
public abstract class RefinedCoffee extends Coffee {
public RefinedCoffee(ICoffeeAdditives additives) {
super(additives);
}
public void checkQuality(){
Random ran=new Random();
System.out.println(String.format("%s 添加%s",additives.getClass().getSimpleName(),ran.nextBoolean()?"太多":"正常"));
}
}
实现层的抽象
public interface ICoffeeAdditives {
void addSomething();
}
//加奶
public class Milk implements ICoffeeAdditives {
@Override
public void addSomething() {
System.out.println("加奶");
}
}
//加糖
public class Sugar implements ICoffeeAdditives {
@Override
public void addSomething() {
System.out.println("加糖");
}
}
使用:
public static void main(String[] args) {
//点两杯加奶的大杯咖啡
RefinedCoffee largeWithMilk=new LargeCoffee(new Milk());
largeWithMilk.orderCoffee(2);
largeWithMilk.checkQuality();
}
访问者模式(Visitor)
适用场景
为对象的集合增加新的功能,而不影响对象及集合本身时,使用vistor模式。
类图
涉及角色
- element,对象
- ObjectStructure,对象集合
- Visitor:访问者,也就是添加新功能的地方。
举例
我们都知道财务都是有账本的,这个账本就可以作为一个对象集合,而它其中的元素有两种,收入和支出;
而查看账本的人可能有这样几种,比如老板,会计事务所的注会,财务主管,等等。而这些人在看账本的时候显然目的和行为是不同的。
元素:
public interface Bill {
void accept(AccountBookViewer v);
}
public class ConsumeBill implements Bill {
private double amount;
private String item;
public ConsumeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
@Override
public void accept(AccountBookViewer v) {
v.view(this);
}
}
public class IncomeBill implements Bill {
private double amount;
private String item;
public IncomeBill(double amount, String item) {
super();
this.amount = amount;
this.item = item;
}
@Override
public void accept(AccountBookViewer v) {
v.view(this);
}
}
访问者类:
public abstract class AccountBookViewer {
//查看消费的单子
abstract void view(ConsumeBill bill);
//查看收入的单子
abstract void view(IncomeBill bill);
}
// 老板
public class Boss extends AccountBookViewer {
private double totalIncome;
private double totalConsume;
@Override
void view(ConsumeBill bill) {
totalConsume += bill.getAmount();
}
@Override
void view(IncomeBill bill) {
totalIncome += bill.getAmount();
}
public double getTotalIncome() {
System.out.println("老板查看一共收入多少,数目是:" + totalIncome);
return totalIncome;
}
public double getTotalConsume() {
System.out.println("老板查看一共花费多少,数目是:" + totalConsume);
return totalConsume;
}
}
// 会计
public class Cpa extends AccountBookViewer {
//注会在看账本时,如果是支出,则如果支出是工资,则需要看应该交的税交了没
public void view(ConsumeBill bill) {
if (bill.getItem().equals("工资")) {
System.out.println("注会查看工资是否交个人所得税。");
}
}
//如果是收入,则所有的收入都要交税
public void view(IncomeBill bill) {
System.out.println("注会查看收入交税了没。");
}
}
最后是对象集合:
public class AccountBook {
//单子列表
private List<Bill> billList = new ArrayList<Bill>();
//添加单子
public void addBill(Bill bill){
billList.add(bill);
}
//供账本的查看者查看账本
public void show(AccountBookViewer viewer){
for (Bill bill : billList) {
bill.accept(viewer);
}
}
}
责任链
适用场景
有多个handler要对一个request进行处理
当有一个以上的对象有机会能够处理某个请求的时候,使用责任链模式。
类图
举例
handler
// 定义hanlder接口
public interface Filter {
void doFilter(String data);
}
class FilterEgg implements Filter {
@Override
public void doFilter(String data) {
//doSomething
}
}
class FilterAoBing implements Filter {
@Override
public void doFilter(String data) {
//doSomething
}
}
...
责任链
public class FilterChain {
List<Filter> filters = new ArrayList<>();
public FilterChain() {
filters.add(new FilterEgg());
filters.add(new FilterAoBing());
...
}
public void processData(String data) {
for (Filter filter : filters) {
filter.doFilter(data);
}
}
}
client
public class Client {
public void handlerRequest(Request request) {
// 得到请求的数据
String data = request.getData();
FilterChain filterChain = new FilterChain();
// 处理数据
filterChain.processData(data);
}
}
引申
这里的责任链是通过for循环遍历各个hander,然后调用处理函数,在Java Web中,有Filter,它的处理就不是通过for循环遍历。
它通过FilterChain与Filter相互调用,逐渐递进的方式来完成各个handler的处理。这种方式的好处是在filter既可以在controller之前处理数据,可以在contrller之后处理数据。