一、简介
结构型模式(Structural Pattern)描述如何将类或者对 象结合在一起形成更大的结构,就像搭积木,可以通过 简单积木的组合形成复杂的、功能更为强大的结构。
-
适配器模式 Adapter Pattern :一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
使用频率:★★★★☆ -
桥接模式 Bridge Pattern :是将抽象部分与它的实现部分分离,使它们都可以独立地变化。
使用频率: ★★★☆☆ -
组合模式 Composite Pattern :将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
使用频率:★★★★☆ -
装饰模式 Decorator Pattern :是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
使用频率:★★★☆☆ -
外观模式 Facade Pattern :外部与一个子系统的通信必须通过一个统一的外观对象进行,为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。
使用频率:★★★★★ -
享元模式 Flyweight Pattern:它使用共享物件,用来尽可能减少内存使用量以及分享资讯给尽可能多的相似物件;它适合用于只是因重复而导致使用无法令人接受的大量内存的大量物件。
使用频率:★☆☆☆☆ -
代理模式 Proxy Pattern:是指一个类别可以作为其它东西的接口。代理者可以作任何东西的接口:网上连接、存储器中的大对象、文件或其它昂贵或无法复制的资源。
使用频率:★★★★☆
二、适配器模式
2.1、类适配器
定义接口
//目标接口
public interface Target {
void request();
}
定义实现
//目标类具体实现
public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("普通功能");
}
}
//具有特殊功能的类 适配者
public class Adaptee {
public void specificRequest() {
System.out.println("特殊功能");
}
}
//类适配器 适配具有特殊功能
public class Adapter extends Adaptee implements Target {
@Override
public void request() {
super.specificRequest();
}
}
测试类
//只使用普通功能
Target concreteTarget = new ConcreteTarget();
concreteTarget.request();//普通功能
/*------------------类适配器-------------------*/
//使用特殊功能 使用适配类
Target adapter = new Adapter();
adapter.request();//特殊功能
2.2、对象适配器
定义接口
//目标接口
public interface Target {
void request();
}
定义实现
//具有特殊功能的类 适配者
public class Adaptee {
public void specificRequest() {
System.out.println("特殊功能");
}
}
//具有特殊功能的类 适配者
public class Adaptee {
public void specificRequest() {
System.out.println("特殊功能");
}
}
//对象适配器 直接关联适配者对象
public class Adapter2 implements Target {
private final Adaptee adaptee;
//通过构造函数传入适配者对象
public Adapter2(Adaptee adaptee) {
this.adaptee=adaptee;
}
@Override
public void request() {
//通过被关联的适配者对象调用适配者中方法
adaptee.specificRequest();
}
}
测试类
/*------------------对象适配器-------------------*/
Target adapter2 = new Adapter2(new Adaptee());
adapter2.request();//特殊功能
2.3、接口适配器
当使用电脑时,监听电脑USB接口使用情况,可以设置电脑USB接口使用监听,可以用接口适配器模式只设置需要USB接口的监听
场景实体定义
//电脑USB接口监听
public interface Listener {
//usb接口1回调
void onUSB1();
//usb接口2回调
void onUSB2();
//usb接口3回调
void onUSB3();
}
//电脑实体类
public class Computer {
private Listener listener;
//设置USB接口监听监听
public void setListener(Listener listener){
this.listener=listener;
}
//使用电脑
public void use(){
if(listener!=null){
//usb1被使用
listener.onUSB1();
//usb2被使用
listener.onUSB2();
//usb3被使用
listener.onUSB3();
}
}
}
//接口适配器类,实现接口并复写方法
public class AdapterListener implements Listener {
@Override
public void onUSB1() {}
@Override
public void onUSB2() {}
@Override
public void onUSB3() {}
}
测试类
/*-------------------接口适配器------------------*/
Computer computer=new Computer();
//只监听USB1监听,可使用接口适配器模式实现
computer.setListener(new AdapterListener(){
@Override
public void onUSB1() {
super.onUSB1();
System.out.println("接口1被使用");
}
});
//使用电脑
computer.use();//接口1被使用
三、桥接模式
3.1、定义抽象类与接口
//人类抽象类
public abstract class Person {
//持有水果的引用
protected Fruits fruits;
//设置吃的水果
public void setFruits(Fruits fruits){
this.fruits=fruits;
}
//吃
protected abstract void eat();
}
//水果接口
public interface Fruits {
//水果名称
String name();
}
3.2、定义具体实现
//中国人实体
public class Chinese extends Person {
@Override
protected void eat() {
if(fruits!=null){
System.out.println("中国人吃"+fruits.name());
}
}
}
//美国人
public class American extends Person {
@Override
public void eat() {
if (fruits != null) {
System.out.println("美国人吃" + fruits.name());
}
}
}
//苹果实体类
public class Apple implements Fruits {
@Override
public String name() {
return "苹果";
}
}
//葡萄实体类
public class Grape implements Fruits {
@Override
public String name() {
return "葡萄";
}
}
3.2、使用桥接模式
//创建中国人 吃苹果
Person chinese=new Chinese();
chinese.setFruits(new Apple());
chinese.eat();//中国人吃苹果
//创建美国人 吃葡萄
Person american=new American();
american.setFruits(new Grape());
american.eat();//美国人吃葡萄
四、组合模式
4.1、组合模式案例
创建员工类
// 员工类
public class Employee {
//姓名
private String name;
//部门
private String dept;
//薪水
private int salary;
private List<Employee> subordinates;
public Employee(String name, String dept, int salary) {
this.name = name;
this.dept = dept;
this.salary = salary;
this.subordinates = new ArrayList<Employee>();
}
public void add(Employee e) {
subordinates.add(e);
}
public void remove(Employee e) {
subordinates.remove(e);
}
//获取下属集合
public List<Employee> getSubordinates(){
return subordinates;
}
}
4.2、使用组合模式
一个公司有一个CEO,CEO手下有销售主管、营销主管,销售主管手下有2个销售员,营销主管手下有2个营销人员。可以使用组合模式构建:
Employee CEO = new Employee("张三", "CEO", 30000);
Employee headSales = new Employee("李四", "销售主管", 20000);
Employee headMarketing = new Employee("李五", "营销主管", 20000);
Employee clerk1 = new Employee("王五", "营销员", 10000);
Employee clerk2 = new Employee("赵六", "营销员", 10000);
Employee salesExecutive1 = new Employee("王五", "销售员", 10000);
Employee salesExecutive2 = new Employee("王芳", "销售员", 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//打印该组织的所有员工
System.out.println(CEO);
for (Employee headEmployee : CEO.getSubordinates()) {
System.out.println(headEmployee);
for (Employee employee : headEmployee.getSubordinates()) {
System.out.println(employee);
}
}
// Employee{name='张三', dept='CEO', salary=30000}
// Employee{name='李四', dept='销售主管', salary=20000}
// Employee{name='王五', dept='销售员', salary=10000}
// Employee{name='王芳', dept='销售员', salary=10000}
// Employee{name='李五', dept='营销主管', salary=20000}
// Employee{name='王五', dept='营销员', salary=10000}
// Employee{name='赵六', dept='营销员', salary=10000}
五、装饰模式
5.1、定义抽象类与接口
定义接口
//基础接口类
public interface Component {
void say();
}
定义抽象装饰类
//装饰类
public abstract class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void say() {
this.component.say();
}
}
5.2、定义具体实现
//具体实现类
public class ConcreteComponent implements Component {
@Override
public void say() {
System.out.println("你好!");
}
}
//具体装饰类
public class ConcreteDecorator extends Decorator{
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void say() {
System.out.println("敬礼!");
super.say();
}
}
5.3、使用装饰模式
//未使用装饰类
ConcreteComponent concretComponent = new ConcreteComponent();
concretComponent.say();//你好!
//使用装饰器
Component component = new ConcreteDecorator(new ConcreteComponent());
component.say();//敬礼! 你好!
六、外观模式
一台电脑有CPU、内存、硬盘等组件组成,可以使用外观模式描述:
6.1、定义组件
//电脑CPU组件对象
public class CPU {
public void start() {System.out.println("CPU已开启");}
public void shutdown() {System.out.println("CPU已关闭");}
}
//电脑硬盘组件对象
public class Disk {
public void start() {System.out.println("硬盘已开启");}
public void shutdown() {System.out.println("硬盘已关闭");}
}
//电脑内存组件对象
public class Memory {
public void start() {System.out.println("内存已开启");}
public void shutdown() {System.out.println("内存已关闭");}
}
6.2、定义外观对象类
将组件组合使用:
//电脑实体(外观类)
public class Computer {
private final CPU cpu;
private final Memory memory;
private final Disk disk;
public Computer() {
cpu = new CPU();
memory = new Memory();
disk = new Disk();
}
public void startup() {
System.out.println("正在打开电脑");
disk.start();
memory.start();
cpu.start();
System.out.println("电脑打开完成");
}
public void shutdown() {
System.out.println("正在关闭电脑");
cpu.shutdown();
memory.shutdown();
disk.shutdown();
System.out.println("电脑关闭完成");
}
}
6.3、使用外观模式
Computer computer=new Computer();
//打开电脑
computer.startup();//正在打开电脑 硬盘已开启 内存已开启 CPU已开启 电脑打开完成
//关闭电脑
computer.shutdown();//正在关闭电脑 CPU已关闭 内存已关闭 硬盘已关闭 电脑关闭完成
七、享元模式
7.1、定义接口与实现
//人类接口
public interface Human {
//吃
void eat();
}
//人类具体实现
public class Per implements Human {
private String name;
private String country;
public Per(String country) {
this.country = country;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void eat() {
System.out.println(country + "人" + name + "吃饭");
}
}
7.2、定义工厂类
//创建人类工厂类
public class PerFactory {
private static final Map<String, Per> pers = new HashMap<String, Per>();
public static Per getPer(String country) {
Per per = pers.get(country);
if (per == null) {
per = new Per(country);
pers.put(country, per);
}
return per;
}
public static int getSize() {
return pers.size();
}
}
7.3、使用享元模式
String[] tags = {"中国", "美国"};
for (int i = 0; i < tags.length; i++) {
for (int j = 0; j < 2; j++) {
Per per = PerFactory.getPer(tags[i]);
per.setName((i == 0 ? "张三" : "Tom") + j);
per.eat();
System.out.println("对象池中对象个数" + PerFactory.getSize());
}
}
//中国人张三0吃饭
//对象池中对象个数1
//中国人张三1吃饭
//对象池中对象个数1
//美国人Tom0吃饭
//对象池中对象个数2
//美国人Tom1吃饭
//对象池中对象个数2
八、代理模式
8.1、静态代理
定义接口与具体实现
//接口
public interface IUserDao {
//保存数据
void save();
}
//具体实现
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("保存数据");
}
}
定义代理对象
//代理对象 静态代理
public class UserDaoProxy {
private IUserDao tag;
public UserDaoProxy(IUserDao tag) {
this.tag = tag;
}
public void save(){
System.out.println("开始事物");
tag.save();//执行目标对象中方法
System.out.println("提交事物");
}
}
使用静态代理
//目标对象
IUserDao userDao = new UserDao();
//代理对象,把目标对象传入代理对象中,建立代理关系
UserDaoProxy userDaoProxy = new UserDaoProxy(userDao);
userDaoProxy.save();//开始事物 保存数据 提交事物
8.2、动态代理
定义接口与具体实现
//接口
public interface IUserDao {
//保存数据
void save();
}
//具体实现
public class UserDao implements IUserDao {
@Override
public void save() {
System.out.println("保存数据");
}
}
定义代理对象
//动态代理Handler
public class UserDaoProxyHandler implements InvocationHandler {
//维护一个目标对象
private Object object;
public UserDaoProxyHandler(Object object){
this.object=object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始事务");
//执行目标对象方法
Object returnValue = method.invoke(object, args);
System.out.println("提交事务");
return returnValue;
}
}
使用动态代理
//目标对象
IUserDao userDao2 = new UserDao();
//指定当前目标对象使用类加载器,获取加载器的方法是固定的
ClassLoader classLoader = userDao2.getClass().getClassLoader();
//目标对象实现的接口的类型,使用泛型方式确认类型
Class<?>[] interfaces = userDao2.getClass().getInterfaces();
//事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
UserDaoProxyHandler userDaoProxyHandler = new UserDaoProxyHandler(userDao2);
//代理对象 给目标对象创建代理对象 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
IUserDao proxy = (IUserDao) Proxy.newProxyInstance(classLoader, interfaces, userDaoProxyHandler);
//执行代理对象中方法
proxy.save();//开始事物 保存数据 提交事物