本篇开始介绍结构型设计模式
前言
与创建型设计模式用于创建对象不同,结构型设计模式通过结构化的方式实现功能的扩展和解耦,通过对象的组合、聚合、继承和接口等机制来定义对象之间的关系,从而实现松耦合和灵活性。
常见的结构性设计模式(具体介绍前四种)
- 代理模式
分为动态代理和静态代理,在不需要了解某一接口或类内部实现细节的情况下,不需要对接口或类进行修改的情况下实现功能的扩展,即在调用前后进行功能扩展。动态代理又分为jdk动态代理和cglib动态代理。
静态代理UML图:

1.1 静态代理:
IBaseOperation 接口 
/**
 * 基础功能接口
 * @author zygswo
 *
 */
public interface IBaseOperation {
	void operation();
}
 
代理对象RealSubject
/**
 * 代理对象
 * @author zygswo
 *
 */
public class RealSubject implements IBaseOperation {
	@Override
	public void operation() {
		System.out.println("real subject print");
	}
}
 
代理类Proxy
/**
 * 代理类
 * @author zygswo
 *
 */
public class Proxy implements IBaseOperation{
	/**
	 * 代理对象
	 */
	RealSubject realSubject;
	
	public Proxy(RealSubject realSubject) {
		this.realSubject = realSubject;
	}
	@Override
	public void operation() {
		System.out.println("pre handler");
		realSubject.operation();
		System.out.println("post handler");
	}
}
 
入口类
public class Main {
	public static void main(String[] args) {
		RealSubject realSubject = new RealSubject();
		Proxy proxy = new Proxy(realSubject);
		proxy.operation();
	}
}
 
效果图
 
1.2 动态代理
 动态代理分为jdk动态代理和cglib动态代理,
 jdk动态代理:基于反射机制实现,需要实现接口然后生成代理类,调用速度较慢,jdk自带
 cglib动态代理:基于字节码机制实现,可以通过继承实现,调用速度较快,需要引入cglib库。
 jdk动态代理例子:
 要实现的接口
public interface MyInterface {
	void getResult();
}
 
实现类:
public class MyClass implements MyInterface {
	@Override
	public void getResult() {
		System.out.println("hello world");
	}
}
 
jdk动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyJvmProxy implements InvocationHandler{
	/**
	 * 代理对象
	 */
	private Object object;
	
	public MyJvmProxy(Object object) {
		this.object = object;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//prehandler
		System.out.println("--------------预处理------------");
		Object obj = method.invoke(object, args);
		//posthandler
		System.out.println("--------------处理完毕------------");
		return obj;
	}
	
	public Object getProxyInstance() {
		return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
	}
}
 
入口类
public class MainEntrance {
	public static void main(String[] args) {
		MyJvmProxy proxy = new MyJvmProxy(new MyClass());
		//传入classloader 使用反射来获取代理对象,之后调用MyJvmProxy来实现功能扩展
		MyInterface myClass = (MyInterface)proxy.getProxyInstance();
		myClass.getResult();
	}
}
 
效果图:
 
cglib 动态代理:
 实现类:
public class MyClass{
	@Override
	public void getResult() {
		System.out.println("hello world");
	}
}
 
cglib动态代理类
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class MyCglibProxy implements MethodInterceptor {
	private Object object;
	public MyCglibProxy(Object object){
		this.object = object;
	}
	 @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
       	System.out.println("--------------预处理------------");
        Object returnValue = proxy.invokeSuper(obj, args);
        System.out.println("--------------处理完毕------------");
        return returnValue;
    }
	
	public Object getProxyInstance() {
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperClass(object.getClass());
		enhancer.setCallback(this);
		return enhancer.create();
	}
} 
 
入口类
public class MainEntrance {
	public static void main(String[] args) {
		MyCglibProxy proxy = new MyCglibProxy(new MyClass());
		//传入classloader 使用反射来获取代理对象,之后调用MyJvmProxy来实现功能扩展
		MyClass myClass = (MyClass)proxy.getProxyInstance();
		myClass.getResult();
	}
}
 
代理模式优点:
 1. 代理对象可以扩展目标对象的功能;
 2. 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
 代理模式缺点:
 1. 增加系统复杂度、降低了代码的可读性
 应用:防火墙、远程(Remote)代理、AOP等。
- 适配器模式
分为类适配器和对象适配器模式,分别通过继承和组合的方式实现功能的扩展。
类适配器模式UML图:

类适配器:
父类:动物,子类:人类,小狗,行为接口类:IBaseBehavior
父类: 
/**
 * 动物类
 * @author zygswo
 *
 */
public class Animal{
	/**
	 * 名字
	 */
	private String name;
	
	/**
	 * 名字
	 */
	private String type;
	
	/**
	 * 年龄
	 */
	private String age;
	public Animal(String name, String type, String age) {
		this.name = name;
		this.type = type;
		this.age = age;
	}
	protected void intro() {
		System.out.println("姓名: " + name + "\t年龄: " + age + "\t种类: " + type);
	}
}
 
行为接口类:
public interface IBaseBehavior {
	void move();
	void bark();
}
 
子类:
public class Dog extends Animal implements IBaseBehavior{
	
	public Dog(String name, String age) {
		super(name,"小狗", age);
	}
	@Override
	public void move() {
		super.intro();
		System.out.println("爬了一步");
	}
	@Override
	public void bark() {
		super.intro();
		System.out.println("吠了一声");
	}
}
public class Human extends Animal implements IBaseBehavior{	
	public Human(String name, String age) {
		super(name,"人类", age);
	}
	@Override
	public void move() {
		super.intro();
		System.out.println("走了一步");
	}
	@Override
	public void bark() {
		super.intro();
		System.out.println("喊了一声");
	}
}
 
入口类:
public class Main {
	public static void main(String[] args) {
		Human human = new Human("章瑜亮", "28");
		Dog dog = new Dog("xxx", "5");
		human.move();
		human.bark();
		dog.move();
		dog.bark();
	}
}
 
效果图
 
对象适配器模式UML图
 
 分为总体类:小狗类、人类
 部分结构类:脚类、心脏类(小狗和人都是有脚、有心脏)
 部分结构类:
 脚类:
public class Foot{
	public Foot() {
	}
	public void run() {
		System.out.println("肢体运动起来了");
	}
}
public class Heart{
	public Heart() {
	}
	public void run() {
		System.out.println("心脏跳动起来了");
	}
}
 
总体类:
 人类
public class Human{
	
	private Heart heart;
	
	private Foot foot;
	public Human(Heart heart, Foot foot) {
		this.heart = heart;
		this.foot = foot;
	}
	public void run() {
		this.heart.run();
		this.foot.run();
		System.out.println("小人活跃了");
	}
}
 
小狗类
public class Dog{
	
	private Heart heart;
	
	private Foot foot;
	public Dog(Heart heart, Foot foot) {
		this.heart = heart;
		this.foot = foot;
	}
	public void run() {
		this.heart.run();
		this.foot.run();
		System.out.println("小狗活跃了");
	}
}
 
入口类:
public class Main {
	public static void main(String[] args) {
		Foot foot = new Foot();
		Heart heart = new Heart();
		Human human = new Human(heart,foot);
		Dog dog = new Dog(heart,foot);
		human.run();
		dog.run();
	}
}
 
效果图:
 
 适配器优点:
 1. 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,无序修改原有结构。
 2. 增加了类的透明性和复用性,将具体业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者类可以在多个不同的系统中复用。
 3. 灵活性和扩展性都非常好,通过使用配置文件可以很方便的更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,符合开闭原则。
 缺点:
适配器缺点:
 类适配器的缺点
 1. 对于 Java 等不支持多重继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者。
 2. 适配者类不能为最终类。
 对象适配器的缺点
 与类适配器模式相比较,在该模式下要在适配器中置换适配者类的某些方法比较麻烦。
 应用场景:用于接口和类不兼容、适配不同数据格式等情况
-  
装饰器模式
通过层层嵌套实现功能扩展,如BufferedReader br2 = new BufferedReader(new InputStreamReader(new FileInputStream(filePath),“gbk”)); 其中FileInputStream是父装饰器,其父类为inputStream,调用InputStreamReader(InputStream is) 构造方法,之后生成InputStreamReader类,其父类为Reader类,再调用new BufferedReader(Reader reader)构造方法作为参数传入,分为抽象构件角色(装饰目标)、具体构件角色(具体的装饰目标)、装饰角色(装饰类)、具体的装饰角色(具体装饰类)抽象构件角色(Component):具体构件类和抽象装饰者类的共同父类。
具体构件角色(ConcreteComponent):抽象构件的子类,装饰者类可以给它增加额外的职责。
装饰角色(Decorator):抽象构件的子类,具体装饰类的父类,用于给具体构件增加职责,但在子类中实现。(可以无)
具体装饰角色(ConcreteDecorator):具体装饰类,定义了一些新的行为,向构件类添加新的特性。 
装饰器模式UML图
 
 抽象构件角色
/**
 * 基础接口
 * @author zygswo
 *
 */
public interface BasePrinter {
	/**
	 * 打印信息
	 * @param msg
	 */
	void print();
}
 
具体构件角色
public class Printer implements BasePrinter{
	private String message;
	
	public Printer(String message) {
		this.message = message;
	}
	
	@Override
	public void print() {
		System.out.println(message);
	}
}
 
具体装饰角色:
/**
 * 具体装饰角色
 * @author zygswo
 *
 */
public class ParentPrinter implements BasePrinter{
	
	/**
	 * 打印日志
	 */
	protected volatile BasePrinter printer;
	
	/**
	 * 构造方法
	 * @param printer
	 */
	public ParentPrinter(BasePrinter printer) {
		this.printer = printer;
	}
	@Override
	public void print() {
		System.out.println("ParentPrinter print start");
		printer.print();
		System.out.println("ParentPrinter print end");
	}
}
/**
 * 具体装饰角色
 * @author zygswo
 *
 */
public class ChildPrinter implements BasePrinter{
	
	/**
	 * 打印日志
	 */
	protected volatile BasePrinter printer;
	
	/**
	 * 构造方法
	 * @param printer
	 */
	public ChildPrinter(BasePrinter printer) {
		this.printer = printer;
	}
	@Override
	public void print() {
		System.out.println("ChildPrinter print start");
		printer.print();
		System.out.println("ChildPrinter print end");
	}
}
 
入口类:
public class Main {
	public static void main(String[] args) {
		BasePrinter myprinter = new Printer("hello world");
		//装饰器层层嵌套
		BasePrinter childPrinter = new ParentPrinter(new ChildPrinter(myprinter));
		childPrinter.print();
	}
}
 
效果图:
 
 装饰器优点:
 1. 对于扩展一个对象的功能,装饰模式比继承更加灵活,不会导致类的个数急剧增加。
 2. 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为。
 3. 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合
 4. 可以创造出很多不同行为的组合,得到更加强大的对象。
 5. 具体构建类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构建类和具体装饰类,原有类库代码无序改变,符合开闭原则。
装饰器缺点:
 1. 在使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值不同,大量的小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能。
 2. 装饰器模式提供了一种比继承更加灵活、机动的解决方案,但同时也意味着比继承更加易于出错,排错也更加困难,对于多次装饰的对象,在调试寻找错误时可能需要逐级排查,较为烦琐。
 应用 :字符字节流等
-  
桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
抽象角色引用实现角色。
例:我们拿支付举例,支付模式和支付渠道是支付的两个维度
支付方式可以抽象出一个支付方式类,将各种方式支付作为其子类,如下图
支付模式与支付方式存在组合关系,可以提供支付模式接口,将具体的模式作为实现类。

桥接模式优点:- 完成了实现和抽象的分离,实现了解耦
 - 增加了系统的可扩展性
桥接模式缺点: - 由于关联关系建立在抽象层,要求开发者一开始就要对抽象层进行设计和编程
 - 桥接模式要求正确识别出系统中的两个独立变化的维度,需要设计者有一定的经验
应用场景:不同数据库的 JDBC 驱动程序、需要在某种统一协议下增加更多组件时(如不同支付渠道下的支付业务) 
 -  
组合模式
将对象组合成树形结构以表示整个部分的层次结构。可以理解为文件目录结构。一个文件夹里有文件夹和文件,其中每一个文件就是叶子节点,不能有其他操作;而文件夹则是一个树枝节点,和根节点一样具有增删改节点的功能,叶子节点则是根目录,可以理解为linux的”/"目录
UML图:

主要角色
抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。在该角色中可以包含所有子类共有行为的声明和实现,在抽象根节点中定义了访问及管理它的子构件的方法,如增加子节点、删除子节点、获取子节点等。
树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
树枝节点可以包含树枝节点,也可以包含叶子节点,它其中有一个集合可以用于存储子节点,实现了在抽象根节点中定义的行为。包括那些访问及管理子构件的方法,在其业务方法中可以递归调用其子节点的业务方法。
叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。
在组合结构中叶子节点没有子节点,它实现了在抽象根节点中定义的行为。
组合模式优点:
1. 结构清晰,为树的结构化表示提供帮助
2. 在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
组合模式缺点:- 需要存在目录式的结构才可以使用
应用场景: 
 - 需要存在目录式的结构才可以使用
 -  
享元模式
摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,从而让我们能在有限的内存容量中载入更多对象
UML图:
主要角色
抽象享元角色(Flyweight):通常是一个接口或抽象类,在抽象享元类中声明了具体享元类公共的方法,这些方法可以向外界提供享元对象的内部数据(内部状态),同时也可以通过这些方法来设置外部数据(外部状态)。享元(Flyweight)模式中存在以下两种状态: 内部状态,即不会随着环境的改变而改变的可共享部分。 外部状态,指随环境改变而改变的不可以共享的部分。享元模式的实现要领就是区分应用中的这两种状态,并将外部状态外部化。可共享的具体享元(Concrete Flyweight)角色:它实现了抽象享元类,称为享元对象;在具体享元类中为内部状态提供了存储空间。通常我们可以结合单例模式来设计具体享元类,为每一个具体享元类提供唯一的享元对象。
非共享的具体享元(Unshared Flyweight)角色:并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可设计为非共享具体享元类;当需要一个非共享具体享元类的对象时可以直接通过实例化创建。
享元工厂(Flyweight Factory)角色:负责创建和管理享元角色。当客户对象请求一个享元对象时,享元工厂检査系统中是否存在符合要求的享元对象,如果存在则提供给客户;如果不存在的话,则创建一个新的享元对象。
 
例如:建立五子棋工厂,生产白/黑子。
GoBangFactory(享元池):通过静态内部类实现 GoBangFactory 单例,生产五子棋棋子的工厂。
 
public class GoBangFactory {
    private Map<String, GoBang> pool = null;
    public GoBangFactory() {
        pool = new HashMap<>();
        WhiteGoBang whiteGoBang = new WhiteGoBang();// 白子
        BlackGoBang blackGoBang = new BlackGoBang();// 黑子
        
        pool.put("w", whiteGoBang);
        pool.put("b", blackGoBang);
    }
    
    private static class SingletonHandler{
        private static final GoBangFactory INSTANCE = new GoBangFactory();
    }
    
    public static GoBangFactory getInstance(){
        return SingletonHandler.INSTANCE;
    }
    public GoBang getGoBang(String key){
        return pool.get(key);
    }
}
 
GoBang(享元类):抽象五子棋类,定义主要功能或特征。
 
public abstract class GoBang {
    
    public abstract String getColor();
    
    public void display(){// 显示棋子颜色
        System.out.println("棋子的颜色:" + getColor());
    }
}
 
WhiteGoBang(共享享元类):白色棋子。
 
public class WhiteGoBang extends GoBang{ 
    @Override
    public String getColor() {
        return "白色";
    }
}
 
BlackGoBang(共享享元类):黑色棋子。
 
public class BlackGoBang extends GoBang{
    @Override
    public String getColor() {
        return "黑色";
    }
}
 
入口类:
public class TestFlyweight {
    @Test
    public void testExample02() {
        GoBangFactory instance = GoBangFactory.getInstance();
        GoBang w1 = instance.getGoBang("w");
        GoBang w2 = instance.getGoBang("w");
        GoBang w3 = instance.getGoBang("w");
        System.out.println("判断黑子是否是同一对象:" + (w1 == w2));
        GoBang b1 = instance.getGoBang("b");
        GoBang b2 = instance.getGoBang("b");
        System.out.println("判断白子是否是同一对象:" + (b1 == b2));
        b1.display();
        b2.display();
        w1.display();
        w2.display();
        w3.display();
    }
}
 
效果图
 
享元模式的优点:
- 极大减少内存中相似或相同对象数量,节约系统资源,提供系统性能。
 - 获取的相同对象为同一对象,保证数据的一致性。
 
享元模式的缺点:
- 如果要自定义对象,不太适合用享元模式。
 - 为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂。
 
应用场景:线程池、数据库连接池、常量池等。
————————————有问题欢迎小伙伴评论区里留言————————————



















