设计模式2(结构型)

本文详细介绍了设计模式中的五种结构型模式:适配器模式、桥接模式、装饰者模式、组合模式和外观模式。适配器模式用于接口转换,桥接模式实现抽象与实现的解耦,装饰者模式动态增加对象功能,组合模式构建部分-整体结构,外观模式简化系统接口。通过实例展示了这些模式的应用及其优缺点。
摘要由CSDN通过智能技术生成

结构型模式

设计模式1(创建型
设计模式3(行为型)

适配器模式

适配器模式(Adapter Pattern)的作用是作为两个不兼容的接口的"转接口",将目标的功能转换为需求的功能。最常见的例子就是电源适配器:家用电源一般都是220V交流电,手机电脑等小型用电器不能直接使用,所以需要电源适配器将其转化为电压较小的直流电来使用,而这个电源适配器就可以理解为适配器模式中的适配器。

优点:使用灵活,提高了代码的复用性,也有利于解耦。

缺点:过多的适配器会使系统复杂且乱。

适配器模式的角色:

  • 资源类(Adaptee):即原始的类
  • 目标类(Target):通过适配转换得到的(接口)
  • 适配器类(Adapter):适配器类,实现转换功能

类适配器模式

类适配器模式通过适 配器继承原有的类来实现。

优点:继承了原来的资源类,可以重写其方法,灵活性有保障;

缺点:java中只能单一继承,同时继承之后直接使用了资源类中的方法,耦合度较高。

如下实例:

  • 需求:给电脑充电,只有220V的交流电直接提供,需要使用适配器将其转换为5V再充电。
  • Volt220:220V电压的资源类;
  • IVolt5:5v电压的接口,具有输出5V电压的方法;
  • VoltAdapter:适配器,继承资源类,实现5V接口,将220V转化为5V;
  • Computer实体类:初始化电量为0;使用充电方法,提供5V电压后即可充满;
  • Client:创建computer并充电。

在这里插入图片描述

Volt220:

/**
 * 220v交流电
 */
public class Volt220 {
    private int volt = 220;
    public int outPut220(){
        return volt;
    }
}

IVolt5:

/**
 * 5v接口
 */
public interface IVolt5 {
    int outPut5();
}

VoltAdapter:

/**
 * 适配器类
 */
public class VoltAdapter extends Volt220 implements IVolt5 {
    @Override
    public int outPut5() {
        // 电压转化
        int volt = outPut220() / 44;
        return volt;
    }
}

Computer:

/**
 * 实体类
 */
public class Computer {
    private int electricity = 0;
    // 充电,需要提供5V电压
    public void recharge(IVolt5 iVolt5){
        int volt = iVolt5.outPut5();
        if(volt == 5){
            System.out.println("开始充电~");
            electricity = 100;
        }else{
            System.out.println("电源不适配~");
        }
    }
    public int getElectricity() {
        return electricity;
    }
}

client:

public class Client {
    public static void main(String[] args) {
        Computer computer = new Computer();
        // 获取当前剩余电量
        System.out.println(computer.getElectricity());
        // 使用适配器充电
        computer.recharge(new VoltAdapter());
        // 充电之后的电量
        System.out.println(computer.getElectricity());
        /**
         * 0
         * 开始充电~
         * 100
         */
    }
}

对象适配器模式

对象适配器使用聚合的方式实现转换,即:适配器不继承资源类,而是将资源类以集合方式提供给适配器(构造器或设置属性)。这样就不再与资源类耦合死,只需要传入对象即可。

对上面的部分代码修改:

在这里插入图片描述

VoltAdapter:

/**
 * 适配器类
 */
public class VoltAdapter implements IVolt5 {
    // 修改部分:不再使用继承,而是使用构造方法传入对象
    private Volt220 volt220;
    public VoltAdapter(Volt220 volt220) {
        this.volt220 = volt220;
    }
    @Override
    public int outPut5() {
        // 电压转化
        int volt = 0;
        // 使用对象中的数据
        if(null != volt220){
            int i = volt220.outPut220();
            volt = i / 44;
        }
        return volt;
    }
}

client:

public class Client {
    public static void main(String[] args) {
        Computer computer = new Computer();
        // 获取当前剩余电量
        System.out.println(computer.getElectricity());
        // 修改:创建资源对象,传给适配器
        Volt220 volt220 = new Volt220();
        // 使用适配器充电
        computer.recharge(new VoltAdapter(volt220));
        // 充电之后的电量
        System.out.println(computer.getElectricity());
        /**
         * 0
         * 开始充电~
         * 100
         */
    }
}

接口适配器

接口适配器使用一个接口(IAdapter)定义的适配器中的抽象方法;使用一个抽象类(AbsAdapter)实现该接口,但是其方法只是默认实现(没有具体实现内容);当其他地方(Client)需使用到适配器中的某个适配操作的时候,只需要创建该抽象类的匿名内部类并重写需要使用的方法即可。

在这里插入图片描述

具体案例

SpringMVC的DispatcherServlet中对于请求根据适配器使用对应的Handler(Controller)中的方法处理请求。

桥接模式

桥接模式(Bridge Parrten)用于把抽象化与实现化解耦,使得二者可以独立变化(所以涉及到了抽象类和接口)。两种类型的类可被结构化改变而互不影响。以实现二者的解耦。

优点:抽象层与实现层分离;便于扩展;利于解耦。

使用于:对于那些不希望使用继承或因为多层次继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。 一个类存在两个独立变化的维度,且这两个维度都需要进行扩展,则可以使用桥接模式。

在这里插入图片描述

有如下实例:

  • 需求:创建角色(Role),每种角色有男性和女性。
  • RoleGender:角色性别接口,实现类为Male和Female;
  • Role:角色抽象类,有子类Tank角色和Mage角色;
  • Client:创建角色。

在这里插入图片描述

RoleGender:

/**
 * 角色性别接口
 */
public interface RoleGender {
    void setGender();
}

Male:

/**
 * 男性
 */
public class Male implements RoleGender{
    @Override
    public void setGender() {
        System.out.println("this Role is a Male");
    }
}

Female:

/**
 * 女性角色
 */
public class Female implements RoleGender{
    @Override
    public void setGender() {
        System.out.println("this Role is a Female");
    }
}

Role:

/**
 * 角色抽象类
 */
public abstract class Role {
    // 聚合性别接口
    private RoleGender roleGender;
    public Role(RoleGender roleGender) {
        this.roleGender = roleGender;
    }
    // 创建角色,调用性别接口中方法
    public void createRole(){
        roleGender.setGender();
    }
}

Mage:

/**
 * Mage角色类
 */
public class Mage extends Role{
    // 调用父类构造器
    public Mage(RoleGender roleGender) {
        super(roleGender);
    }
    // 重写父类方法:创建角色
    @Override
    public void createRole() {
        super.createRole();
        System.out.println("Role: Mage is created");
    }
}

Tank:

/**
 * Tank角色
 */
public class Tank extends Role{
    public Tank(RoleGender roleGender) {
        super(roleGender);
    }
    @Override
    public void createRole() {
        super.createRole();
        System.out.println("Role: Tank is created");
    }
}

Client:

public class client {
    public static void main(String[] args) {
        // 创建女性Mage
        Mage mage = new Mage(new Female());
        mage.createRole();
        // this Role is a Female
        // Role: Mage is created
        
        // 创建男性Tank
        Tank tank = new Tank(new Male());
        tank.createRole();
        // this Role is a Male
        // Role: Tank is created
    }
}

如上,通过聚合将抽象化与实现化解耦,实体类与接口实现类分开,相互独立。无论之后新增多少角色,都只需要创建Role子类即可,且只需要关系实体本身的内容,无需设置性别。性别有创建时传入的接口实现类确定。

装饰者模式

装饰者模式(Decorator Pattern)指在不改变对象原来结构的前提下,为其添加新的功能。

装饰者模式可以理解为:一个人,穿衣服,再穿裤子,再穿鞋子,最后戴帽子…这样一个一层层往外包的过程(套娃),也就是一层层装饰的过程,这个过程对于之前的对象不做任何改变,但是其又有了新的功能。

正因为是层层装饰,所以在装饰者类中,都包含有一个被装饰着对象。

装饰者模式中的角色:

  • 装饰者(类)
  • 被装饰者(类)

优点:装饰者与被装饰者相互独立,也意味着二者可以自行扩展却不会影响到对方,所以有利于扩展和解耦。

缺点:装饰层数多了比较复杂(臃肿)。

比如下面的场景:

  • 需求:有一间房(Room),对房屋进行装修,可以是装空调、电视、沙发、书柜等等。
  • Room:为一个总的大类(抽象类),其他所有都继承自该类;
  • LivingRoom,BedRoom:具体的被装饰者(类),只有先创建他们才能进行装饰;
  • RoomDecorator:装饰者类(抽象类),其中包含有Room属性就是被装饰者;其他具体的装饰着都继承自该类;
  • TVAdecorator、SofaDecorator、AirConditionerAdecorator:具体的装饰者类,用于装饰被装饰者;
  • Client:创建被装饰者,并对其进行装饰,在对其装饰的时候只需创建该装饰着并将被装饰者传递给它即可。

在这里插入图片描述

Room:

/**
 * Room类
 */
public abstract class Room {
    // 描述
    private String dec;
    public String getDec() {
        return dec;
    }
    public void setDec(String dec) {
        this.dec = dec;
    }
    // 该方法用于描述当前房间被修饰的情况
    public abstract void RoomDec();
}

LivingRoom:

/**
 * 客厅
 */
public class LivingRoom extends Room {
    // 设置房间描述
    public LivingRoom() {
        setDec("this is livingroom");
    }
    @Override
    public void RoomDec() {
        System.out.println(super.getDec());
    }
}

BedRoom:

/**
 * 卧室
 */
public class Bedroom extends Room{
    // 设置房间描述
    public Bedroom(){
        setDec("this is bedroom");
    }

    @Override
    public void RoomDec() {
        System.out.println(super.getDec());
    }
}

RoomDecorator:

/**
 * 装饰着类
 */
public abstract class RoomDecorator extends Room {
    // 被装饰者对象
    private Room room;
    public RoomDecorator(Room room){
        this.room = room;
    }
    // 输出当前类的被装饰情况
    @Override
    public void RoomDec() {
        room.RoomDec();
        System.out.println(getDec());
    }
}

AirConditionerAdecorator:

/**
 * 修饰一个空调
 */
public class AirConditionerAdecorator extends RoomDecorator{
    // 初始化时,传入被修饰对象
    public AirConditionerAdecorator(Room room) {
        super(room);
        setDec("add air condition");
    }
}

TVAdecorator:

/**
 * 修饰一个电视
 */
public class TVAdecorator extends RoomDecorator{
    public TVAdecorator(Room room) {
        super(room);
        setDec("add TV");
    }
}

SofaDecorator:

/**
 * 修饰一个沙发
 */
public class SofaDecorator extends RoomDecorator{
    public SofaDecorator(Room room) {
        super(room);
        setDec("add sofa");
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        // 1.创建一个客厅
        LivingRoom livingRoom = new LivingRoom();
        livingRoom.RoomDec();
        System.out.println("==========");
        // 2.为客厅修饰一个空调
        AirConditionerAdecorator conditioner = new AirConditionerAdecorator(livingRoom);
        conditioner.RoomDec();
        System.out.println("==========");
        // 3.为客厅在装饰一个电视
        TVAdecorator tvAdecorator = new TVAdecorator(conditioner);
        tvAdecorator.RoomDec();
        System.out.println("==========");
        // 4.创建一个卧室
        Bedroom bedroom = new Bedroom();
        // 5.为卧室装饰一个沙发
        SofaDecorator sofaDecorator = new SofaDecorator(bedroom);
        sofaDecorator.RoomDec();
        /**
         * output:
         * this is livingroom
         * ==========
         * this is livingroom
         * add air condition
         * ==========
         * this is livingroom
         * add air condition
         * add TV
         * ==========
         * this is bedroom
         * add sofa
         */
    }
}

通过上面实现装饰着模式,在添加房间或者添加装饰类的时候,只需要创建个自的具体类就可以直接实现装饰。比如添加一个厨房(Kitchen),只需要创建厨房类即可,再使用已有的装饰类进行装饰,以实现解耦。

JDK中的使用

比如IO流中:

在这里插入图片描述

如图:

  • InputStream作为一个抽象类,其他类都继承自该类(对比上面例子中的Room);
  • FileInoutStream、ByteArrayInputStream作为InputStream的直接继承(对比上面的LivingRoom等);
  • FilterInputStream:装饰者类,其中包含有一个InputStream属性(被装饰者,对比RoomDecorator);
  • BuferedInputStream等类继承自FilterInputStream,作为具体的装饰者,以实现在原来的InputStream的基础上添加功能。

组合模式

组合模式(Composite Parrten),又称为部分整体模式。组合模式使用树形结构将各个层次的对象组合起来构成一个整体。需要将这些对象抽象为一个对象,再继承这个抽象对象以实现组合。适用于整体与部分关系的场景(如:国家-省份、公司-分公司等)

优点:模块化的调用;节点调整方便。

如下:

  • 需求:对国家区域面积统计(只统计国家、省份、城市)。
  • AreaComposite:抽象类,其他类都继承自该类以便构建整体,包括有属性:name、square;方法:add(添加子节点)、remove(删除子节点)、print(输出信息);
  • Nation:国家节点,包括有一个list保存省份信息(聚合),对方法重写;
  • Province:省份节点,包括有一个list保存城市信息(聚合),对方法重写;
  • city:叶子节点,没有下级,所以就没有list;

在这里插入图片描述

AreaComposite:

/**
 * 国家地域抽象类
 */
public abstract class AreaComposite {
    private String name;
    private double square;

    public AreaComposite(String name, double square) {
        this.name = name;
        this.square = square;
    }
    public String getName() {
        return name;
    }
    public double getSquare() {
        return square;
    }
    // 添加区域
    public void add(AreaComposite areaComposite){
        // do nothing
    }
    // 删除区域
    public void remove(AreaComposite areaComposite){
        // 都nothing
    }
    // 输出区域信息
    public abstract void print();
}

Nation:

/**
 * 国家节点
 */
public class Nation extends AreaComposite{
    // 该集合用于存放国家下一级区域(省份)
    private List<AreaComposite> areaCompositeList = new ArrayList<>();
    // 设置信息
    public Nation(String name, double square) {
        super(name, square);
    }
    // 重写添加方法,往集合中添加省份
    @Override
    public void add(AreaComposite areaComposite) {
        areaCompositeList.add(areaComposite);
    }
    // 删除省份
    @Override
    public void remove(AreaComposite areaComposite) {
        areaCompositeList.remove(areaComposite);
    }
    // 输出信息
    @Override
    public void print() {
        System.out.println("===========Nation:" + getName() + " " + getSquare() + "平方千米==============");
        for (AreaComposite areaComposite : areaCompositeList) {
            areaComposite.print();
        }
    }
}

Province:

/**
 * 省份信息(与国家类似)
 */
public class Province extends AreaComposite{
    // 该集合用于存放省份的下一级(城市)
    private List<AreaComposite> areaCompositeList = new ArrayList<>();

    public Province(String name, double square) {
        super(name, square);
    }
    @Override
    public void add(AreaComposite areaComposite) {
        areaCompositeList.add(areaComposite);
    }
    @Override
    public void remove(AreaComposite areaComposite) {
        areaCompositeList.remove(areaComposite);
    }
    @Override
    public void print() {
        System.out.println("===========Province:" + getName()  + " " + getSquare() + "平方千米=============");
        for (AreaComposite areaComposite : areaCompositeList) {
            areaComposite.print();
        }
    }
}

City:

/**
 * 城市
 */
public class City extends AreaComposite{

     // 作为叶子节点,所以不需要集合添加下一级

    public City(String name, double square) {
        super(name, square);
    }
    @Override
    public void print() {
        System.out.println(getName() + ":" + getSquare() + "平方千米");
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        Nation nation = new Nation("中国", 9600000);
        Province province1 = new Province("浙江", 105600);
        Province province2 = new Province("湖北", 185900);
        City city1 = new City("杭州", 16596.00);
        City city2 = new City("温州", 11784.00);
        City city3 = new City("武汉", 8406.00);
        // 国家添加省份
        nation.add(province1);
        nation.add(province2);
        // 省份添加城市
        province1.add(city1);
        province1.add(city2);
        province2.add(city3);
        // 输出信息
        nation.print();
        /**
         * ===========Nation:中国 9600000.0平方千米==============
         * ===========Province:浙江 105600.0平方千米=============
         * 杭州:16596.0平方千米
         * 温州:11784.0平方千米
         * ===========Province:湖北 185900.0平方千米=============
         * 武汉:8406.0平方千米
         */
    }
}

JDK中的使用

比如HashMap:

在这里插入图片描述

如上图:

  • Map接口作为根节点(比较上述案例的AreaComposite,接口和抽象类的区别);
  • AbstractMap:中间层,对部分方法实现;
  • HashMap继承AbstractMap,同时实现Map接口,并对方法进行实现(如put方法);
  • Node:作为叶子节点;
  • 区别在于,由于HashMap底层是数组+链表+红黑树,由Node节点组成,所以在HashMap中也就不可能在看到上面案例中的List来存放孩子节点。

外观模式

外观模式(Facade Pattren)隐藏了系统的复杂性,对外提供这个系统的访问接口(不是指java中interface),系统的具体功能部分不对外暴露,客户端只需访问这些接口就能实现功能。

使用场景:当程序、体统复杂性高的时候,用户调用具体的功能实现就会很繁杂,使用外观模式将其隐藏,用户调用暴露的就扣就能实现这些功能,简化了客户端的操作。

优点:提高系统的灵活性;简化客户端操作;

缺点:不符合OCP原则,牵一发而动全身,修改功能需要做大量的修改。

如下场景:

  • 需求:家庭环境,通过简单的操作完成一些复杂的组合功能:如回家时开灯制冷;看电影时灯光调整电视开启;睡觉时关灯空调进入睡眠模式等。
  • Home:代表家,一个系统,包括的所有的功能组件并隐藏了这些复杂的功能,只对外暴露一些接口以实现调用;
  • tv、light等都是功能组件,数量多的功能繁杂;
  • client:客户端,用户只需要操作Home系统提供的接口就能完成而很多的复杂的功能。

在这里插入图片描述

Home:

/**
 * Home系统类
 */
public class Home {
    private AirCondition airCondition;
    private Light light;
    private TV tv;
    private WashingMachine washingMachine;

    public Home() {
        this.airCondition = AirCondition.getInstance();
        this.light = Light.getInstance();
        this.tv = TV.getInstance();
        this.washingMachine = WashingMachine.getInstance();
    }
    // 回家(夏天:开灯制冷)
    public void backHome(){
        light.open();
        light.Brighten();
        airCondition.open();
        airCondition.Refrigeration();
    }
    // 休闲
    public void casual(){
        tv.open();
        tv.movie();
        light.dim();
        washingMachine.autoWashing();
    }
    // 睡觉
    public void sleep(){
        tv.close();
        light.close();
        airCondition.sleep();
    }
}

AirCondition:

/**
 * 空调
 */
public class AirCondition {
    // 使用单例
    private AirCondition(){}
    private static final AirCondition instance = new AirCondition();
    public static AirCondition getInstance(){
        return instance;
    }
    // 开启空调
    public void open(){
        System.out.println("AirCondition open~");
    }
    // 关闭空调
    public void close(){
        System.out.println("AirCondition close~");
    }
    // 制热
    public void Heating(){
        System.out.println("AirCondition Heating~");
    }
    // 制冷
    public void Refrigeration(){
        System.out.println("AirCondition Refrigeration~");
    }
    // 睡眠模式
    public void sleep(){
        System.out.println("AirCondition will sleep~");
    }
}

Light:

/**
 * 灯光
 */
public class Light {
    // 使用单例
    private Light(){}
    private static final Light instance = new Light();
    public static Light getInstance(){
        return instance;
    }
    // 开灯
    public void open(){
        System.out.println("light open~");
    }
    // 关灯
    public void close(){
        System.out.println("light close~");
    }
    // 调暗
    public void dim(){
        System.out.println("light dim~");
    }
    // 调亮
    public void Brighten(){
        System.out.println("light brighten~");
    }
}

TV:

/**
 * 电视
 */
public class TV {
    // 使用单例
    private TV(){}
    private static final TV instance = new TV();
    public static TV getInstance(){
        return instance;
    }
    // 开启电视
    public void open(){
        System.out.println("TV open~");
    }
    // 关闭电视
    public void close(){
        System.out.println("TV close~");
    }
    // 看电影
    public void movie(){
        System.out.println("TV Play movie");
    }
}

WashingMachine:

/**
 * 洗衣机
 */
public class WashingMachine {
    // 使用单例
    private WashingMachine(){}
    private static final WashingMachine instance = new WashingMachine();
    public static WashingMachine getInstance(){
        return instance;
    }
    // 自动洗衣
    public void autoWashing (){
        System.out.println("WashingMachine running~");
    }
}

client:

public class client {
    public static void main(String[] args) {
        Home home = new Home();
        home.backHome();
        System.out.println("----------");
        home.casual();
        System.out.println("----------");
        home.sleep();
    }
}

享元模式

享元模式(Flyweight Pattern)主要作用是减少对象的产生,以减少内存的占用和性能的提高。突出对对象的重用,如果已经存在了一个相同的对象则使用之,没有再去创建一个新的。

使用场景:java中的各种池(数据库连接池、线程池等);字符串常量池中不存重复的值;Integer中valueOf()方法对于-128-127之间数值的复用等;

状态:在享元模式中有两种状态,内部状态和外部状态:

  • 内部状态:表示确定的、共享的信息,不会随环境的变化而变化,比如多人对战游戏中的地图;
  • 外部状态:表示不确定的、不可共享的,随外部环境改变而改变,比如多人对战游戏中每个玩家的信息;

优点:减少大量的内存创建,减少内存的消耗。

缺点:需要控制内外部状态,否则就会造成混乱。

在这里插入图片描述

在享元模式中,由上图中的部分组成:Flyweight——抽象享元类;ConcreteFlyweight——具体的享元类,继承自抽象享元类(内部状态,共享的部分,也即可重复利用的部分);UnConcreteFlyweight——外部状态,不共享;FlyweightFactory——享元工厂,根据情况创建享元对象;Client——客户端,使用享元工厂获得和使用对象。

如下实例:

  • 需求:现有一个多人对战游戏,每一个房间使用一个地图且地图不重复;要求同一房间内的玩家使用同一份地图(外部状态),而房间中可以同时存在多个玩家(内部状态);
  • GameFlyweight:抽象享元类,其中map为内部状态,player为外部状态;
  • GameContreteFlyweight:具体享元类,重写了play方法为具体游戏操作;
  • GameFactory:享元类工厂,根据用户输入的map名称,决定是否加入已有的游戏,还是新建游戏;
  • Clinet:客户端,用于游玩游戏。

在这里插入图片描述

GameFlyweight:

/**
 * 抽象享元角色
 */
public abstract class GameFlyweight {
    // 外部状态
    private List<String> player;
    // 内部状态
    private String map;
    // 成员操作方法
    public GameFlyweight(String map) {
        this.map = map;
    }
    public List<String> getPlayer() {
        return player;
    }
    // 添加玩家
    public void addPlayer(String p) {
        if(this.player == null){
            this.player = new ArrayList<String>();
        }
        player.add(p);
    }
    public String getMap() {
        return map;
    }
    public void setMap(String map) {
        this.map = map;
    }
    // 游玩游戏抽象方法
    public abstract void play();
}

GameConcreteFlyweight:

/**
 * 具体享元角色
 */
public class GameConcreteFlyweight extends GameFlyweight{
    // 创建地图的游戏,并设置地图名称
    public GameConcreteFlyweight(String map) {
        super(map);
        System.out.println( " game with 《" + getMap() + "》 create");
    }
    // 游玩
    @Override
    public void play() {
        System.out.println(getPlayer() + " is playing with " + getMap());
    }
}

GameFactory:

/**
 * 享元工厂:用于生产享元角色
 */
public class GameFactory {
    // map存放享元对象
    public static Map<String,GameFlyweight> map = new HashMap<>();
    // 判断需求的game是否已经存在,如果存在直接使用,不存在则新建
    public static GameFlyweight entryGame(String mapName){
        GameFlyweight game = map.get(mapName);
        if(game == null){
            game = new GameConcreteFlyweight(mapName);
            map.put(mapName, game);
        }
        return game;
    }
    // 获取已有的享元角色的数量
    public static int getMapSize(){
        return map.size();
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        // tom开启一个map1的游戏
        GameFlyweight map1 = GameFactory.entryGame("map1");
        map1.addPlayer("tom");
        map1.play();
        // jack开启一个map2的游戏
        GameFlyweight map2 = GameFactory.entryGame("map2");
        map2.addPlayer("jack");
        map2.play();
        // jery开启一个map1的游戏
        GameFlyweight map3 = GameFactory.entryGame("map1");
        map3.addPlayer("jery");
        map3.play();
        // lucy开启一个map1的游戏
        GameFlyweight map4 = GameFactory.entryGame("map1");
        map4.addPlayer("lucy");
        map4.play();
        // 输出已近开启的游戏数量
        System.out.println(GameFactory.getMapSize());
        /**
         *  game with 《map1》 create
         *  game with 《map1》 create
         * [tom] is playing with map1
         *  game with 《map2》 create
         * [jack] is playing with map2
         * [tom, jery] is playing with map1
         * [tom, jery, lucy] is playing with map1
         * 2
         */
    }
}

从Client的输出来看:尽管有4名玩家加入游戏,但是只创建了2份游戏对象,其中map1中有三名玩家,从而达到了减少对象的产生和对象的重用。

JDK中的使用

如Integer中valueOf方法的IntegerCache,对于-128~127中的数,会直接使用缓存中的数而不是新建对象,所以也是享元模式的一种一种体现。

源码如下:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

事实上,Integer.valueOf()方法是包装类的一个装箱过程,比如Integer i = 1;就会自动调用该方法返回一个Integer对象(JDK1.5之开始才有);而intValue()方法则是拆箱过程,将对象转化为数值。

所以如下:

Integer i = 1;
Integer j = Integer.valueOf(1);
System.out.println(i == j); // true

另外可以查看Byte、Short、Character、Long的ValueOf()方法,都具有cache的操作。

再看下面的代码:

        Integer a = 15;
        Integer b = Integer.valueOf(15);
        Integer c = new Integer(15);
        int d = 15;

        System.out.println(a == b); // true
        System.out.println(a == c); // false
        System.out.println(a == d); // true
        System.out.println(b == d); // true
        System.out.println(b == c); // falseL
        System.out.println(c == d); // true

//        Integer a = 200;
//        Integer b = Integer.valueOf(200);
//        Integer c = new Integer(200);
//        int d = 200;
//
//        System.out.println(a == b); // false
//        System.out.println(a == c); // false
//        System.out.println(a == d); // true
//        System.out.println(b == d); // true
//        System.out.println(b == c); // false
//        System.out.println(c == d); // true

结合拆箱装箱和上面的分析也能够清晰的判断上面代码的结果:

  • 与int比较会自动拆箱,直接比较数值,所以肯定true;
  • Integer与new的Integer对象比较(==),结果肯定为false(引用赋值除外);
  • 对于-127~128范围内的Integer,调用了valueOf()方法之后一定true;超出之后为false;
  • 对于equals()方法比较会进行装箱之后在比较,毕竟只有对象才有方法之说。

代理模式

代理模式(Proxy Pattern)为:用一个对象(代理对象)代表另一个对象(目标对象)执行其功能,并可以在不改变目标对象的基础上实现对其功能的增强。

代理模式可以分为:静态代理和动态代理。动态代理又可以分为:JDK代理和Cglib代理。

优点:扩展性高,低侵入;

缺点:由于增加的代理类,处理时间增加;是系统更复杂。

静态代理

静态代理使用接口实现目标对象和代理对象。看如下代码:

  • 需求:有一个类只能画圆,要求不改变该类的情况下,对其进行扩展,能有其他操作。
  • ICircle:接口,目标类和代理类都实现该接口,达到重写其中方法的目的;
  • Target:目标类,只能实现画圆操作;
  • CircleProxy:代理类:聚合了目标类,调用目标类中的方法,并添加自己的方法;
  • Client:客户端,实现具体操作。

在这里插入图片描述

ICircle:

/** 
* 接口,,代理类和目标类都继承该类,都实现接口中的方法 
*/
public interface ICircle {    
    void draw();
}

Target:

/** 
* 目标类 
*/
public class Target implements ICircle {    
    // 实现方法    
    @Override    
    public void draw() {
        System.out.println("draw a circle~");    
    }
}

CircleProxy:

/** 
* 目标类 
*/
public class CircleProxy implements ICircle{    
    // 聚合目标类    
    private ICircle circle;    
    public CircleProxy(ICircle circle) {        
        this.circle = circle;    
    }    
    // 实现接口中的方法,并对目标类的中方法进行调用    
    @Override    
    public void draw() {        
        System.out.println("prepare a paper");        
        circle.draw();        
        System.out.println("fill color");    
    }
}

Client:

public class Client {    
    public static void main(String[] args) {        
        // 使用代理类,即实现了目标类中的方法,有对其进行功能的加强        
        CircleProxy circleProxy = new CircleProxy(new Target());        
        circleProxy.draw();        
        /**         
        * prepare a paper        
        * draw a circle~         
        * fill color         
        */    
    }
}

静态代理的缺点:将会创建很多的代理类,并在在添加方法的时候,需要修改接口和其所有实现类,所以维护起来麻烦。

动态代理(JDK)

动态代理中,代理对象不需要实现接口,而目标对象需要实现接口,否则无法使用;代理对象是通过反射动态生成的,通过代理对象调用目标对象接口中的的方法。所以又称为接口代理。

使用的包是:java.lang.reflect.Proxy,使用newProxyInstance()方法。

jdk动态代理实现过程:

  1. 创建目标对象接口;
  2. 实现该对象(即为目标对象);
  3. 创建代理类生成类,聚合一个目标对象;
  4. 在代理类生成类中添加方法,返回值为Object;方法中调用Proxy类中的newProxyInstance()方法,给定其参数(参数意义见下面代码注释);
  5. 在客户端创建目标对象、代理对象生成对象;调用代理对象生成对象中的方法获得代理对象;调用方法。

在这里插入图片描述

ICircle、Target同静态代理

ProxyFactory:

/** 
* 代理类工厂,生成代理类 
*/
public class ProxyFactory {    
    // 维护的目标对象    
    private ICircle circle;    
    public ProxyFactory(ICircle iCircle) {        
        this.circle = iCircle;    
    }    
    // 使用该方法返回代理对象    
    public Object getProxyInstance(){        
        /**         
        * 使用java.lang.reflect.Proxy类中的newProxyInstance方法         
        * 参数:         
        * 第一个参数:ClassLoader,目标对象的类加载器         
        * 第二个参数:Class<?>[] interfaces :目标对象实现的接口         
        * 第三个参数:InvocationHandler,调用的目标方法的处理          
        */        
        return Proxy.newProxyInstance(                
            circle.getClass().getClassLoader(),                
            circle.getClass().getInterfaces(),                
            new InvocationHandler(){                    
                /**                    
                *                     
                * @param proxy   代理对象                     
                * @param method  调用目标对象的方法                     
                * @param args    调用方法的参数                     
                * @return        调用方法的返回值                     
                * @throws Throwable                    
                */                    
                @Override                    
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                        
                    // 增强操作                        
                    System.out.println("prepare paper");                        
                    // 调用的目标对象的方法                        
                    Object res = method.invoke(circle, args);                        
                    // 增强方法                        
                    System.out.println("draw over");                        
                    return res;                    
                }                
            }                
        );    
    }
}

Client:

public class Client {    
    public static void main(String[] args) {        
        // 目标对象        
        Target target = new Target();       
        // 获得代理类工厂对象        
        ProxyFactory proxyFactory = new ProxyFactory(target);        
        // 获得代理对象,需要强转且只能使用接口接收        
        ICircle instance = (ICircle)proxyFactory.getProxyInstance();        
        // 调用方法        
        instance.draw();        
        /**         
        * prepare paper         
        * draw a circle~         
        * draw over         
        */    
    }
}

动态代理(Cglib)

Cglib动态代理的目标对象不需要实现接口,通过目标类的子类来实现代理。因此目标对象不能final修饰,方法为final或static不能被代理。

Cglib使用流程:

  1. 引入cglib的jar包;
  2. 创建目标类;
  3. 创建代理对象生成对象(ProxyFactory),实现MethodInterceptor接口;
  4. ProxyFactory中聚合目标对象;
  5. ProxyFactory创建方法getInstance()返回代理对象;
  6. 实现接口中的方法intercept(),调用目标对象的方法,并增加功能;
  7. 客户端获得代理对象,调用方法实现代理。

在这里插入图片描述

CircleTarget:

/** 
* 目标类 
*/
public class CircleTarget {    
    public void draw(){        
        System.out.println("draw a circle");    
    }
}

ProxyFactory:

/** 
* 代理对象生成类 
*/
public class ProxyFactory implements MethodInterceptor {    
    // 目标对象    
    private Object target;    
    public ProxyFactory(Object target) {        
        this.target = target;    
    }    
    // 获取代理对象    
    public Object getProxyInstance(){        
    // 1.创建工具类        
        Enhancer enhancer = new Enhancer();        
        // 2.设置父类        
        enhancer.setSuperclass(target.getClass());        
        // 3.设置回调函数        
        enhancer.setCallback(this);       
        // 4.创建子类对象(代理对象)        
        return enhancer.create();    
    }    
    // 实现intercept方法,在方法中调用目标对象中的方法   
    @Override    
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {        
        System.out.println("cglib is prepare");        
        Object invoke = method.invoke(target, objects);        
        System.out.println("cglib over");        
        return invoke;    
    }
}

Client:

public class Client {    
    public static void main(String[] args) {        
        // 目标对象        
        CircleTarget circleTarget = new CircleTarget();        
        // 获取代理对象        
        CircleTarget proxyInstance = (CircleTarget)new ProxyFactory(circleTarget).getProxyInstance();        
        // 方法执行        
        proxyInstance.draw();        
        /**         
        * cglib is prepare         
        * draw a circle         
        * cglib over         
        */    
    }
}

具体的使用:Spring的AOP

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值