结构型设计模式

  • 从程序的结构上解决模块之间的耦合问题

6.4.1. 代理模式

  • 代理模式也被称为委托模式。
  • 定义:为其他对象提供一种代理以控制对这个对象的访问。
    在这里插入图片描述
    在这里插入图片描述

1. 抽象主题类

  • 抽象主题类具有真实主题类和代理的共同接口方法,共同的方法就是购买
package com.example.mode2;

public interface IShop {
    void buy();
}

2. 真实主题类

  • 购买者就是我本人,实现了IShop提供的buy()方法
package com.example.mode2;

public class Ning implements IShop{
    @Override
    public void buy() {
        System.out.println("购买");
    }
}

3. 代理类

  • 代理类同样需要实现IShop接口,并且要持有被代理者,在buy()方法中调用被代理者的buy()方法
package com.example.mode2;

public class purchaseProxy implements IShop{
    private IShop mShop;
    public purchaseProxy(IShop iShop){
        mShop = iShop;
    }

    @Override
    public void buy() {
        mShop.buy();
    }
}

4. 客户端实现

  • 客户端类的代码就是代理类包含了真实主题类(被代理者),最终调用真实主题类实现的方法。
package com.example.mode2;

public class Client {
    public static void main(String[] args) {
        IShop ning = new Ning();
        IShop purchasing = new purchaseProxy(ning);
        purchasing.buy();
    }
}

动态代理的实现

  • 从编码角度,代理模式分为静态代理和动态代理。
  • 上面的例子是静态代理,在代码运行前就已经存在了代理类的class编译文件
  • 而动态代理则是在代码运行时通过反射来动态地生成代理类的对象。
  • Java提供了动态的代理接口InvocationHandler,实现该接口需要重写invoke方法。请添加图片描述
package com.example.mode2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class DynamicPurchasing implements InvocationHandler {
    
    private Object object;
    public DynamicPurchasing(Object object){
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(object , args);
        //获取方法名
        if(method.getName().equals("buy")){
            System.out.println("Ning在买东西");
        }
        return result;
    }
}
  • 接下来修改客户端类的代码
package com.example.mode2;

import java.lang.reflect.Proxy;

public class Client {
    public static void main(String[] args) {
        //创建Ning
        IShop ning = new Ning();
        //创建动态代理
        DynamicPurchasing mDynamicPurchasing = new DynamicPurchasing(ning);
        //创建Ning的ClassLoader
        ClassLoader loader = ning.getClass().getClassLoader();
        //动态创建代理类
        IShop purchasing = (IShop) Proxy.newProxyInstance(loader , new Class[]{IShop.class} , mDynamicPurchasing);
        purchasing.buy();
    }
}

在这里插入图片描述

6.4.2 装饰模式

  • 其在不改变类文件和使用继承的情况下,动态地拓展了一个对象的功能,它是继承的替代方案之一。
  • 定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。请添加图片描述
  • 杨过本身会全真剑法。洪七公和欧阳锋教了他打狗棒法和蛤蟆功。
    这样杨过就学会了三个功法。洪七公和欧阳锋就起到了装饰杨过的作用。

1. 抽象组件

  • 首先定义一个武侠的抽象类,里面有使用武功的抽象方法。
public abstract class Swordsman{
    public abstract void attackMagic();
}

2. 组件的具体实现类

  • 被装饰的具体对象,这里就是被教授武学的具体武侠,也就是杨过。杨过作为武侠当然也会武学
public class YangGuo extends Swordsman{
    @Override
    public void attackMagic(){
        //杨过初始的武学是全真剑法
        System.out.println("使用剑法");
    }
}

3. 抽象装饰类

  • 抽象装饰者保持了一个对抽象组件的引用,方便调用被装饰对象中的方法。
  • 在这个例子中,就是武学前辈要持有武侠的引用,方便教授他武学并使它融会贯通。
public abstract class Master extends Swordsman{
    private Swordsman mSwordsman;
    public Master(Swordman mSwordsman){
        this.mSwordsman = mSwordsman;
    }
    @Override
    public void attackMagic(){
        mSwordsman.attackMagic();
    }
}

4. 装饰者具体实现类

  • 用两个装饰者具体实现类,他们负责向杨过传授新的武功
public class HongQiGong extends Master{
    public HongQiGong(Swordsman mSwordsman){
        super(mSwordsman);
    }
    public void teachAttackMagic(){
        System.out.println("洪七公教授打狗棒法");
        System.out.println("杨过使用打狗棒法");
    }
    @Override
    public void attackMagic(){
        super.attackMaigc();
        teachAttackMagic();
}

public class OuYangFeng extends Master{
    public OuYangFeng(Swordsman mSwordsman){
        super(mSwordsman);
    }
    public void teachAttackMagic(){
        System.out.println("欧阳锋使用蛤蟆功");
        System.out.println("杨过使用蛤蟆功");
    }
    @Override
    public void attackMagic(){
        super.attackMagic();
        teachAttackMagic();
    }
}

5. 客户端调用代码

public class Client{
    public static void main(String[] args){
        YangGuo mYangGuo = new YangGuo();
        HongQiGong mHongQiGong = new HongQiGong(mYangGuo);
        mHongQiGong.attackMagic();
        OuYangFeng mOuYangFeng = new OuYangFeng(mYangGuo);
        mOuYangFeng.attackMagic();
    }
}
  • 装饰者具体实现类是最后的对象
    在这里插入图片描述

6.4.3 外观模式

  • 外观模式也叫做门面模式。无论是做SDK还是封装API大多都会用到外观模式。
  • 它通过一个外观类使得整个系统的结构只有一个统一的高层接口,这样能降低用户的使用成本。
  • 定义:要求一个子系统的外部与内部的通信必须通过一个统一的对象进行。此模式必须提供一个高层的接口,使得子系统更易于使用。
    请添加图片描述
  • 本节还举武侠的例子。首先,我们把武侠张无忌当作一个系统,张无忌作为一个武侠,它本身分为3个系统,分别是招式、内功和经脉。

1. 子系统类

  • 张无忌有很多武学和内功,怎么将它们搭配并对外界隐藏呢?
//1.子系统招式
public class ZhaoShi{
    public void TaiJiQuan(){
        System.out.print("使用招式太极拳");
    }
    public void QiShangQuan(){
        System.out.print("使用招式七伤拳");
    }
    public void ShengHuo(){
        System.out.print("使用招式圣火令");
    }
}

//2.子系统内功
public class NeiGong{
    public void JiuYang(){
        System.out.print("使用九阳神功");
    }
    public void QianKun(){
        System.out.print("使用乾坤大挪移");
    }
}

//3.子系统经脉
public class JingMai{
    public void jingmai(){
        System.out.print("开启经脉");
    }
}

2. 外观类

  • 这里的外观类就是张无忌,他负责将自己的招式、内功和经脉通过不同的情况合理地运用
  • 初始化外观类的同时将各个子系统创建好。
  • 很明显,这里将内部子系统进行了搭配。
  • 使用七伤拳,就需要开启经脉,使用内功九阳神功,然后再使用七伤拳。
public class ZhangWuJi{
    private JingMai jingMai;
    private ZhaoShi zhaoShi;
    private NeiGong neiGong;
    public ZhangWuJi(){
        jingMai = new JingMai();
        zhaoshi = new ZhaoShi();
        neiGong = new NeiGong();
    }
    
    //使用乾坤大挪移
    public void QianKun(){
        jingMai.jingmai();//开启经脉
        neiGong.QianKun();//使用内功乾坤大挪移
    }
    
    //使用七伤拳
    public void QiShang(){
        jingMai.jingmai();//开启经脉
        neiGong.JiuYang();//使用内功九阳神功
        zhaoShi.QiShangQuan();//使用招式七伤拳
    }
}

3. 客户端调用

  • 外观模式本身就是将子系统的逻辑和交互隐藏起来,为用户提供一个高层次的接口,使得系统更加易用,同时隐藏了具体的实现。
  • 这样即使具体的子系统发生了变化,用户也不会感知到。
public class Client{
    public static void main(String[] args){
        ZhangWuJi zhangWuJi = new ZhangWuJi();
        zhangWuJi.QianKun();
        zhangWuJi.QiShang();
    }
}

在这里插入图片描述

6.4.4 享元模式

  • 享元模式是池技术的重要实现方式,它可以减少应用程序的创建,降低程序内存的占用,提高程序的性能。
  • 定义:使用共享对象有效地支持大量细粒度的对象
  • 细粒度对象:不可避免地使用对象数量多且性质相近的。这些对象分为两部分:内部状态和外部状态。内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变。外部状态是对象依赖的一个标记,它是随环境改变而改变的并且不可共享的状态。
    请添加图片描述
  • 某网上商城卖商品,如果每个用户
  • 那么订单量会生成很多商品对象,更何况商品的种类繁多,这样就会产生OOM。因此我们用享元模式来对商品创建进行优化。

1. 抽象享元角色

  • 抽象享元角色是一个商品接口,它定义了showGoodsPrice方法来展示商品的价格
package com.example.mode2;

public interface IGoods {
    public void showGoodsPrice(String name);
}

2. 具体享元角色

  • 定义类Goods,它实现了IGoods接口,并实现了showGoodsPrice方法
  • 其中name为内部状态,version为外部状态。 showGoodsPrice会根据version的不同打印出不同的价格。
package com.example.mode2;

public class Goods implements IGoods{
    private String name;//名称
    private String version;//版本
    Goods(String name){
        this.name = name;
    }
    
    @Override
    public void showGoodsPrice(String name) {
        if(version.equals("32G")){
            System.out.println("价格为5199元");
        }else if(version.equals("128G")){
            System.out.println("价格为5999元");
        }
            
    }
}

3. 享元工厂

  • 用来创建Goods对象。
  • 通过Map容器来存储Goods对象,将内部状态name作为Map的key,以便标识Goods对象。
  • 如果包含,则使用缓存对象,不包含就新创建Goods对象。
package com.example.mode2;

import java.util.HashMap;
import java.util.Map;

public class GoodsFactroy {
    //静态变量
    private static Map<String , Goods> pool = new HashMap<String , Goods>();
    public static Goods getGoods(String name){
        if(pool.containsKey(name)){
            System.out.println("使用缓存,key为:" + name);
            return pool.get(name);
        }else{
            Goods goods = new Goods(name);
            pool.put(name , goods);
            System.out.println("创建商品,key为:" + name);
            return goods;
        }
   
    }
}

4. 在客户端调用

package com.example.mode2;

public class Client2 {
    public static void main(String[] args) {
        Goods goods1 = GoodsFactroy.getGoods("iphone7");
        goods1.showGoodsPrice("32G");
        
        Goods goods2 = GoodsFactroy.getGoods("iphone7");
        goods2.showGoodsPrice("32G");
        
        Goods goods3 = GoodsFactroy.getGoods("iphone7");
        goods3.showGoodsPrice("128G");
    }
}

在这里插入图片描述

  • 享元模式的使用场景:系统中存在大量的相似对象。需要缓冲池的场景。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值