结构型模式
简介
将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式。
结构型模式分为:代理模式、适配器模式、装饰模式、桥接模式、外观模式、享元模式、组合模式
结构型模式分类
(1) 代理模式
说明:给某对象提供一个代理以控制对该对象的访问。访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
优点:
在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
将客户端与目标对象分离,降低了系统的耦合度;
缺点:
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度;
1 ) 静态代理实现
目标接口代码
package com.threadtest.designpattern;
public interface Subject {
void request();
}
目标接口实现类代码
package com.threadtest.designpattern;
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("执行了目标类请求!");
}
}
代理类实现代码
package com.threadtest.designpattern;
public class SubjectProxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if(realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("执行目标对象前执行的业务!");
}
public void postRequest() {
System.out.println("执行目标对象后执行的业务!");
}
}
代理执行源码
package com.threadtest.designpattern;
public class ProxyTest {
public static void main(String[] args) {
SubjectProxy proxy = new SubjectProxy();
proxy.request();
}
}
2 ) 动态代理实现
a. JDK动态代理
package com.threadtest.designpattern;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class SubjectInvocationHandler implements InvocationHandler {
private RealSubject realSubject;
public SubjectInvocationHandler(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行目标代码前业务执行...!");
method.invoke(realSubject, args);
System.out.println("执行目标代码后业务执行...!");
return null;
}
}
代理执行源码
package com.threadtest.designpattern;
import java.lang.reflect.Proxy;
public class ProxyTest {
public static void main(String[] args) {
Subject subject = (Subject) Proxy.newProxyInstance(ProxyTest.class.getClassLoader(), new Class[]{Subject.class}, new SubjectInvocationHandler(new RealSubject()));
subject.request("李白");
subject.request2();
}
}
b. Cglib动态代理
向pom.xml引入cglib包
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
目标类源码
package com.design;
public class RealSubject {
public void request(String name) {
System.out.println(name + " 执行了目标类请求!");
}
public void request2() {
System.out.println("执行了目标类请求2!");
}
}
定义拦截器源码
package com.design;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class SubjectMethodInterceptor implements MethodInterceptor {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行目标类前的业务!");
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
定义动态代理工厂
package com.design;
import net.sf.cglib.proxy.Enhancer;
public class ProxyFactory {
public static Object getCglibDynProxy(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new SubjectMethodInterceptor());
return enhancer.create();
}
}
定义具体执行类
package com.design;
public class ProxyTest {
public static void main(String[] args) {
RealSubject realSubject = (RealSubject) ProxyFactory.getCglibDynProxy(new RealSubject());
realSubject.request("陶渊明");
realSubject.request2();
}
}
(2) 适配器模式
说明:将一个类的接口转换成客户希望的另外一个接口,使不兼容的接口在一起工作。
1 ) 类适配器模式
目标接口源码
package com.design;
public interface Bird {
void sayBirdLanguage();
}
适配者类源码
package com.design;
public class Pig {
public void sayPigLanguage() {
System.out.println("我是猪,所以我说猪语!");
}
}
适配器类源码
package com.design;
public class ClassLanguageAdapter extends Pig implements Bird {
public void sayBirdLanguage() {
System.out.println("转化为鸟语!");
sayPigLanguage();
}
}
适配器执行源码
package com.design;
public class LanguageTest {
public static void main(String[] args) {
Bird bird = new ClassLanguageAdapter();
bird.sayBirdLanguage();
}
}
2 ) 对象适配器模式
适配器源码
package com.design;
public class ObjectLanguageAdapter implements Bird {
private Pig pig;
public ObjectLanguageAdapter(Pig pig) {
this.pig = pig;
}
public void sayBirdLanguage() {
System.out.println("转化为鸟语!");
pig.sayPigLanguage();
}
}
配置器执行代码
package com.design;
public class LanguageTest {
public static void main(String[] args) {
Bird bird = new ObjectLanguageAdapter(new Pig());
bird.sayBirdLanguage();
}
}
(3) 桥接模式
说明:用组合关系将抽象与实现分离,使它们可以独立变化。
优点:
由于抽象与实现分离,所以扩展能力强;
其实现细节对客户透明。
缺点
由于聚合关系建立在抽象层,要求开发者针对抽象化进行设计与编程,这增加了系统的理解与设计难度。
实现化角色源码
package com.design;
public interface Fruit {
void taste();
}
具体实现化角色源码
package com.design;
public class Apple implements Fruit {
public void taste() {
System.out.println("我的味道有点苦涩!");
}
}
抽象化角色源码
package com.design;
public abstract class AbstractShop {
protected Fruit fruit;
public AbstractShop(Fruit fruit) {
this.fruit = fruit;
}
protected abstract void sale();
}
扩展抽象化角色源码
package com.design;
public class AppleShop extends AbstractShop {
public AppleShop(Fruit fruit) {
super(fruit);
}
protected void sale() {
System.out.println("我们这里卖的是苹果!");
fruit.taste();
}
}
(4) 装饰模式
说明:在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
优点:
采用装饰模式扩展对象的功能比采用继承方式更加灵活。
可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
缺点
装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
抽象构建角色源码
package com.design;
public interface Person {
void work();
}
具体构建角色源码
package com.design;
public class Woman implements Person {
public void work() {
System.out.println("我的工作是文员!");
}
}
抽象装饰角色源码
package com.design;
public class DecoratorPerson implements Person {
private Person person;
public DecoratorPerson(Person person) {
this.person = person;
}
public void work() {
person.work();
}
}
具体装饰角色源码
package com.design;
public class DecoratorPersonTraffic extends DecoratorPerson {
public DecoratorPersonTraffic(Person person) {
super(person);
}
public void work() {
super.work();
backHome();
}
public void backHome() {
System.out.println("下班回家!");
}
}
装饰器测试执行源码
package com.design;
public class DecoratorTest {
public static void main(String[] args) {
Person person = new Woman();
person.work();
Person person1 = new DecoratorPersonTraffic(person);
person1.work();
}
}
(5) 外观模式
说明:通过为多个复杂的子系统提供一个一致的统一的接口,降低程序复杂度,提高代码可维护性。
优点
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。
缺点
不能很好地限制客户使用子系统类。
增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
外观角色源码
package com.design;
public class LunchPlan {
private FruitNeed fruitNeed = new FruitNeed();
private MeatNeed meatNeed = new MeatNeed();
private TeaNeed teaNeed = new TeaNeed();
public void eating() {
fruitNeed.eat();
meatNeed.taste();
teaNeed.drink();
}
}
子系统角色源码
package com.design;
public class FruitNeed {
public void eat() {
System.out.println("用餐时需要吃水果!");
}
}
package com.design;
public class MeatNeed {
public void taste() {
System.out.println("用餐时,需要品尝牛肉的味道!");
}
}
package com.design;
public class TeaNeed {
public void drink() {
System.out.println("用晚餐,还需要喝点奶茶!");
}
}
外观执行源码
package com.design;
public class FacadeTest {
public static void main(String[] args) {
LunchPlan lunchPlan = new LunchPlan();
lunchPlan.eating();
}
}
(6) 享元模式
说明:通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销。
优点
相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。
缺点:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。
读取享元模式的外部状态会使得运行时间稍微变长。
抽象享元角色源码
package com.design;
public interface Transport {
public void operate(UnSharedCustomer unSharedCustomer);
}
具体享元角色源码
package com.design;
public class Airplane implements Transport {
private String basicInfo;
public Airplane(String info) {
this.basicInfo = info;
}
public void operate(UnSharedCustomer unSharedCustomer) {
System.out.println("我是飞机,我已经在运营了!");
if(unSharedCustomer.availuableUse()) {
System.out.println("登机成功!");
System.out.println(String.format("乘客信息, 姓名: %s, 年龄: %s", unSharedCustomer.getUserName(), unSharedCustomer.getAge()));
} else {
System.out.println("未满18岁不能登机!");
}
}
}
非享元角色源码
package com.design;
public class CustomerInfo implements UnSharedCustomer {
private String userName;
private int age;
public CustomerInfo(String userName, int age) {
this.userName = userName;
this.age = age;
}
@Override
public String getUserName() {
return userName;
}
@Override
public int getAge() {
return age;
}
@Override
public boolean availuableUse() {
if(age >= 18) {
return true;
}
return false;
}
}
享元工厂角色源码
package com.design;
import java.util.HashMap;
import java.util.Map;
public class TransportFactory {
private TransportFactory(){}
private static final TransportFactory factory = new TransportFactory();
public final Map<String, Airplane> transportMap = new HashMap<>();
public static Transport getTransport(String name) {
Airplane airplane = factory.transportMap.get(name);
if(airplane == null) {
airplane = new Airplane(name);
factory.transportMap.put(name, airplane);
}
return airplane;
}
}
享元执行源码
package com.design;
public class FlyweightTest {
public static void main(String[] args) {
Transport transport = TransportFactory.getTransport("hb546333");
UnSharedCustomer unSharedCustomer = new CustomerInfo("黎明", 52);
UnSharedCustomer unSharedCustomer1 = new CustomerInfo("李白", 14);
transport.operate(unSharedCustomer);
transport.operate(unSharedCustomer1);
}
}
(7) 组合模式