23种设计模式归纳总结——结构型
Posted 化作孤岛的瓜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了23种设计模式归纳总结——结构型相关的知识,希望对你有一定的参考价值。
目录
主要解决“类或对象的组合或组装”问题,将不同功能代码解耦
5.代理模式
在不直接修改原有类的前提下对原有类的方法功能进行扩展。
通过引入代理类(构参带入)来给原始类附加功能
示例:
定义一个绘制api DrawApi, 需要在绘制前后进行一些扩展功能(打点,监控等)
1.静态代理
缺点:
1:如果原始类有多个方法,我们需要在代理类中,将原始类中的所有的方法,都重新实现一遍
2:如果存在多个被代理类(比如下面的绘制 和 测算两个类) 都需要通过代理模式添加特殊功能,
则需要针对每个被代理类都创建一个代理类
//定义一个绘制接口(原始类)
interface DrawApi
void draw();
void refresh();
//定义具体的圆形绘制方案,被代理类
static class CircleDraw implements DrawApi
@Override
public void draw()
System.out.println("画一个圆");
@Override
public void refresh()
System.out.println("刷新");
//定义一个测算接口,原始类
interface CalculateApi
void calculate();
//定义一个绘制代理类,代理实现具体的绘制功能,代理类
static class DrawProxy implements DrawApi
//被代理的绘制方案
private DrawApi target;
//构造器
public DrawProxy()
this.target = new CircleDraw();
@Override
public void draw()
before();
target.draw();
after();
@Override
public void refresh()
//缺点:我们需要在代理类中,将原始类中的所有的方法,都重新实现一遍
private void after()
System.out.println("执行后 做一些事情");
private void before()
System.out.println("执行前 做一些事情");
public static void main(String[] args)
//使用静态代理
new DrawProxy().draw();
// 如果需要扩展其他原始类,则这样行不通
//new DrawProxy(new CalculateApi()).draw();
//线程也用到了代理模式,传入不同的runnable实现
//new Thread(实现runnable接口的实例化对象).start();
2.动态代理
主要是为了解决静态代理的两个缺点
//定义一个绘制接口,原始类
interface DrawApi
void draw();
void refresh();
//定义具体的圆形绘制方案,被代理类
static class CircleDraw implements DrawApi
@Override
public void draw()
System.out.println("画一个圆");
@Override
public void refresh()
System.out.println("刷新");
//定义一个测算接口,原始类
interface CalculateApi
void calculate();
//定义圆形的测算方案,被代理类
static class CircleCalculate implements CalculateApi
@Override
public void calculate()
System.out.println("测量圆的尺寸");
//动态代理实现类
//优点:不需要将将原始类中的所有的方法,都重新实现一遍
public static class DrawProxy
public DrawProxy()
public Object createProxy(Object proxiedObject)
Class<?>[] interfaces = proxiedObject.getClass().getInterfaces();
DynamicProxyHandler handler = new DynamicProxyHandler(proxiedObject);
return Proxy.newProxyInstance(proxiedObject.getClass()
.getClassLoader(), interfaces, handler);
//代理类的实现处理接口
private class DynamicProxyHandler implements InvocationHandler
private final Object drawImpl;
public DynamicProxyHandler(Object drawImpl)
this.drawImpl = drawImpl;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
before();
Object result = method.invoke(drawImpl, args);
String apiName = drawImpl.getClass().getName() + ":" + method.getName();
System.out.println("执行方法:" + apiName);
after();
return result;
private void after()
System.out.println("执行后 做一些事情");
private void before()
System.out.println("执行前 做一些事情");
public static void main(String[] args)
DrawProxy proxy = new DrawProxy();
DrawApi drawApi = (DrawApi) proxy.createProxy(new CircleDraw());
drawApi.draw();
//完美解决 原始类有多个的问题
CalculateApi calculateApi = (CalculateApi) proxy.createProxy(new CircleCalculate());
calculateApi.calculate();
6.桥接模式
概念:
与代理模式容易混淆。一个类存在两个(或多个)独立变化的维度,我们通过组合的方式,让这两个(或多个)维度可以独立进行扩展。
通俗的说,就是一个类如果有多个属性需要变化,在进行扩展(继承)时会导致新增的子类指数增加,所以需要通过桥接模式将继承改为组合的方式,
将属性的维度解藕出来,将抽象与原本复杂的实现分离,提高扩展能力。
区别:
与代理模式相比,假设将一个纬度抽象以后,执行的方法命名为a()
代理模式侧重于 通过代理的方式扩展被代理类的功能
桥接模式侧重于 通过组合的方式让不同的纬度的原始类可以搭配使用
示例:
以android画图举例,需要提供一个接口DrawApi,可以绘制三种颜色,与三种形状,普通的做法是通过子类实现不同的组合,一共有9种子类。
这里把颜色解藕然后通过构参带入,把形状解藕为通过子类实现,使其变为两个自由的纬度,通过组合关系(也就是桥梁)任意组合在一起,
可以大幅减少实现的类型。
//颜色抽象化,原始类
interface Color
String getColor();
//具体的红色,原始类实现
static class RedColor implements Color
@Override
public String getColor()
return "红色";
//创建桥接的实现接口。画图api
static abstract class DrawApi
protected Color mColor;
public DrawApi(final Color color)
mColor = color;
abstract void draw();
//桥接模式实现类
static class CircleDraw extends DrawApi
public CircleDraw(final Color color)
super(color);
@Override
void draw()
System.out.println("画一个 " + mColor.getColor() + " 的圆形");
public static void main(String[] args)
DrawApi drawApi = new CircleDraw(new RedColor());
drawApi.draw();
7.装饰器模式
概念:
装饰器模式是指给一个类增强一些方法,对其做一些包装,但是不会影响改变原本类。
异同:
1.与代理模式的区别
装饰器模式看起来跟代理模式特别的相似,但是
对于代理模式来说可以作出一些操作改变原有代码,也就是说带有侵入性。
装饰者模式侧重于添加添加额外的功能职责,也可以重叠使用。
2.与继承的区别:
继承设计子类,是在编译时静态决定的,通过组合的做法扩展对象,可以在运行时动态地进行扩展
装饰者模式通过组合和委托,可以在运行时动态地为对象加上新的行为
示例:
需要一个画图api,在draw之后,需要设置颜色和形状
//画图api抽象类
static abstract class DrawComponent
abstract void draw();
static class DrawApi extends DrawComponent
@Override
void draw()
System.out.println("画图");
/**
* Decorator,装饰抽象类,继承了Component
* 从外类来扩展Component类的功能,但对于Component来说,
* 是无需知道Decorator的存在的
*/
static abstract class DrawDecorator extends DrawComponent
protected DrawComponent component;
public DrawComponent getComponent()
return component;
public void setComponent(final DrawComponent component)
this.component = component;
@Override
void draw()
if (component != null)
component.draw();
static class ShapeDrawDecorator extends DrawDecorator
@Override
void draw()
super.draw();
System.out.println("设置形状");
static class ColorDrawDecorator extends DrawDecorator
@Override
void draw()
super.draw();
System.out.println("设置颜色");
public static void main(String[] args)
DrawApi drawApi = new DrawApi();
ShapeDrawDecorator shapeDrawDecorator = new ShapeDrawDecorator();
shapeDrawDecorator.setComponent(drawApi);
ColorDrawDecorator colorDrawDecorator = new ColorDrawDecorator();
colorDrawDecorator.setComponent(shapeDrawDecorator);
colorDrawDecorator.draw();
8.适配器模式
概念:
将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。(比如Android中列表的Adapter)
分为类适配器,对象适配器
IDrawApi为绘图接口,Printer为不兼容IDrawApi的打印器功能类,Adapter通过继承或者组合兼容两种功能
问题:
应该选择类适配器还是对象适配器?
如果Printer接口并不多,那两种实现方式都可以。
如果Printer接口很多,而且Printer和IDrawApi接口定义大部分都相同,那我们推荐使用类适配器,因为Adaptor复用父类Printer的接口,比起对象适配器的实现方式,Adaptor的代码量要少一些。
如果Printer接口很多,而且Printer和IDrawApi接口定义大部分都不相同,那我们推荐使用对象适配器,因为组合结构相对于继承更加灵活。
示例:
类适配器:基于继承实现
/**
* 类适配器,基于继承
*/
interface IDrawApi
void draw();
/**
* 打印
*/
static class Printer
public void print()
System.out.println("打印");
/**
* Adapter既可以绘画也可以打印
*/
static class Adapter extends Printer implements IDrawApi
@Override
public void draw()
System.out.println("绘画");
@Override
public void print()
super.print();
public static void main(String[] args)
Adapter adapter = new Adapter();
adapter.draw();
adapter.print();
示例:
对象适配器:基于组合实现
/**
* 绘图
*/
interface IDrawApi
void draw();
/**
* 打印
*/
static class Printer
public void print()
System.out.println("打印");
/**
* Adapter既可以绘画也可以打印
*/
static class Adapter implements IDrawApi
private Printer mPrinter;
public Adapter(final Printer printer)
mPrinter = printer;
@Override
public void draw()
System.out.println("绘画");
public void print()
mPrinter.print();
public static void main(String[] args)
Adapter adapter = new Adapter(new Printer());
adapter.draw();
adapter.print();
9.门面模式
概念:
也叫外观模式,为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。
简单来说,就是将多个接口调用替换为一个门面接口调用。
static class TextHelper
public void print()
System.out.println("写字");
static class DrawHelper
public void print()
System.out.println("画图");
static class PrinterManager
TextHelper mTextHelper;
DrawHelper mDrawHelper;
public PrinterManager(final TextHelper textHelper, final DrawHelper drawHelper)
mTextHelper = textHelper;
mDrawHelper = drawHelper;
public void print()
if (mTextHelper != null)
mTextHelper.print();
if (mDrawHelper != null)
mDrawHelper.print();
public static void main(String[] args)
PrinterManager printerManager = new PrinterManager(new TextHelper(), new DrawHelper());
printerManager.print();
10.组合模式
概念:
将一组对象组织(Compose)成树形结构,以表示一种“部分-整体”的层次结构。
组合让客户端(在很多设计模式书籍中,“客户端”代指代码的使用者。)可以统一单个对象和组合对象的处理逻辑。
示例:
希望在内存中构建整个公司的人员架构图(部门、子部门、员工的隶属关系),
并且提供接口计算出部门的薪资成本(隶属于这个部门的所有员工的薪资和)。
采用组合的方式可以更方便的处理员工与部门的工资计算。
//人力资源
static abstract class HumanResource
protected long id;
protected double salary;
public HumanResource(long id)
this.id = id;
public long getId()
return id;
public abstract double calculateSalary();
//员工
static class Employee extends HumanResource
public Employee(long id, double salary)
super(id);
this.salary = salary;
@Override
public double calculateSalary()
return salary;
//部门
public static class Department extends HumanResource
private List<HumanResource> subNodes = new ArrayList<>();
public Department(long id)
super(id);
@Override
public double calculateSalary()
double totalSalary = 0;
for (HumanResource hr : subNodes)
totalSalary += hr.calculateSalary();
this.salary = totalSalary;
return totalSalary;
public void addSubNode(HumanResource hr)
subNodes.add(hr);
public static void main(String[] args)
//it部门
Department itDepartment = new Department(1);
itDepartment.addSubNode(new Employee(1, 5000));
itDepartment.addSubNode(new Employee(2, 5100));
itDepartment.addSubNode(new Employee(3, 5200));
itDepartment.addSubNode(new Employee(4, 5300));
itDepartment.addSubNode(new Employee(5, 5400));
//客户部门
Department customerDepartment = new Department(1);
customerDepartment.addSubNode(new Employee(1, 1000));
customerDepartment.addSubNode(new Employee(2, 1100));
customerDepartment.addSubNode(new Employee(3, 1200));
customerDepartment.addSubNode(new Employee(4, 1300));
customerDepartment.addSubNode(new Employee(5, 1400));
//总薪水
double salaryAll = itDepartment.calculateSalary() + customerDepartment.calculateSalary();
System.out.println("总薪水:" + salaryAll);
11.享元模式
概念:
当一个系统中存在大量重复对象的时候,如果这些重复的对象是不可变对象,
我们就可以利用享元模式将对象设计成享元,在内存中只保留一份实例,供多处代码引用
示例:
直播间需要实现弹幕表情功能,每一个弹幕的id和表情是固定的,但是x,y坐标不同。
// 享元类,弹幕
static class BarrageUnit
public int id;
private String face;
public BarrageUnit(final int id, final String face)
this.id = id;
this.face = face;
@Override
public String toString()
return "BarrageUnit" +
"id=" + id +
", face='" + face + '\\'' +
'';
//弹幕类
static class Barrage
public BarrageUnit mBarrageUnit;
public float x, y;
public Barrage(final BarrageUnit barrageUnit, final float x, final float y)
mBarrageUnit = barrageUnit;
this.x = x;
this.y = y;
@Override
public String toString()
return "Barrage" +
"mBarrageUnit=" + mBarrageUnit +
", x=" + x +
", y=" + y +
'';
static class ChessPieceUnitFactory
private static final Map<Integer, BarrageUnit> barrages = new HashMap<>();
static
barrages.put(1, new BarrageUnit(1, "微笑"));
barrages.put(2, new BarrageUnit(2, "愤怒"));
barrages.put(3, new BarrageUnit(3, "狗头"));
//...省略初始化其他弹幕的代码...
public static BarrageUnit getChessPiece(int barrageId)
return barrages.get(barrageId);
static class LiveRoom
private final Map<Integer, Barrage> barrages = new HashMap<>();
//展示表情弹幕
public void showFace(int barrageId, int toPositionX, int toPositionY)
barrages.put(barrageId, new Barrage(
ChessPieceUnitFactory.getChessPiece(barrageId), toPositionX, toPositionX));
public void draw()
System.out.println("绘制弹幕:" + barrages.toString());
public static void main(String[] args)
LiveRoom liveRoom = new LiveRoom();
liveRoom.showFace(1,100,100);
liveRoom.showFace(2,200,200);
liveRoom.showFace(3,300,300);
liveRoom.draw();
以上是关于23种设计模式归纳总结——结构型的主要内容,如果未能解决你的问题,请参考以下文章