浅析23中设计模式(2/3) - 结构型模式

发布于:2022-12-17 ⋅ 阅读:(356) ⋅ 点赞:(0)

适配器模式

目的:将某个类的接口转换成客户端所期望的另一个接口表示,目的就是兼容性,让两个原本接口不匹配的两个类可以协同工作。

分类:

Ⅰ. 类适配器,适配器继承要适配的类

  1. 用到了继承,导致src类的方法在适配器中都暴露出来了,增加了使用成本

  2. 但是由于继承,所以可以重新src类的方法,增加了灵活性

实现过程:

  • 编写src类

    public class Voltage220V {
        public int output220V() {
            int src = 220;
            System.out.println("电压为" + src);
            return src;
        }
    }
  • 编写目标抽象接口,并编写适配器类实现该接口以达到适配目的

    public interface Voltage5V {
        public int output5V();
    }
    ​
    public class VoltageAdapter extends Voltage220V implements Voltage5V{
        @Override
        public int output5V() {
            int src = output220V();
            int dstV = src/44;
            return dstV;
        }
    }
  • 需要适配的对象

    ​
    public class Phone {
        public void charging(Voltage5V voltage5V){
            if(voltage5V.output5V() == 5){
                System.out.println("电压为5v,可以充电...");
            }else{
                System.out.println("不可以充电");
            }
        }
    }
  • 测试

    public class Test {
        public static void main(String[] args) {
            VoltageAdapter voltageAdapter = new VoltageAdapter();
            Phone phone = new Phone();
            phone.charging(voltageAdapter);
        }
    }

    Ⅱ、对象适配器模式

  • 改动就是将适配器中的继承改变为聚合关系,通过构造器传入Voltage220V对象,以满足合成复用原则
public class VoltageAdapter implements Voltage5V{

    private Voltage220V voltage220V;

    public VoltageAdapter(Voltage220V voltage220V) {
        this.voltage220V = voltage220V;
    }
    @Override
    public int output5V() {
        if(voltage220V!= null){
            int src = voltage220V.output220V();
            int dstV = src/44;
            return dstV;
        }else {
            return 0;
        }
    }
}

Ⅲ、接口适配器模式

  1. 选择性地实现接口方法

  2. 创建接口

    public interface InterfaceMed {
        
        public void m1();
        
        public void m2();
        
        public void m3();
        
        public void m4();
    }

  3. 创建适配器抽象类继承接口并将方法默认实现

    public class AbsAdapter implements InterfaceMed{
        //默认实现
        @Override
        public void m1() {
    ​
        }
    ​
        @Override
        public void m2() {
    ​
        }
    ​
        @Override
        public void m3() {
    ​
        }
    ​
        @Override
        public void m4() {
    ​
        }
    }

4. 客户端可以通过匿名内部类的方式进行选择性重写调用

public class Client {
    public static void main(String[] args) {
        //匿名内部类,选择性覆盖父类方法
        new AbsAdapter(){
            @Override
            public void m1() {
                //只想使用m1方法
                System.out.println("使用了m1方法");
            }
        };
    }
}

桥接模式

目的:将实现与抽象放在两个不同的类层次之中,使两个层次可以独立改变。当然可以拓展为多个,主要也是应用于两个或多个同等级的接口。

底层仍然和java的动态绑定机制有着密切联系。

代码模拟

  • 定义品牌接口

    public interface Brand {
        void open();
    ​
        void close();
    ​
        void call();
    }
  • 定义具体品牌并实现方法

    public class XiaoMi implements Brand{
        @Override
        public void open() {
            System.out.println("xiaomi开机");
        }
    ​
        @Override
        public void close() {
            System.out.println("xiaomi关机");
        }
    ​
        @Override
        public void call() {
            System.out.println("xiaomi电话");
        }
    }
  • 定义电话抽象类,并聚合品牌接口,这样传入品牌即可调用对应的方法,类似一种桥梁的感觉

    public abstract class Phone {
        private Brand brand;
    ​
        public Phone(Brand brand) {
            this.brand = brand;
        }
    ​
        protected void open(){
            brand.open();
        }
    ​
        protected void close(){
            brand.close();
        }
    ​
        protected void call(){
            brand.call();
        }
    }
  • 编写手机类型继承电话抽象类

    public class FolderPhone extends Phone{
    ​
        public FolderPhone(Brand brand) {
            super(brand);
        }
    ​
        public void open(){
            super.open();
            System.out.println("折叠样式手机");
        }
    ​
        public void close(){
            super.close();
            System.out.println("折叠样式手机");
        }
    ​
        public void call(){
            super.call();
            System.out.println("折叠样式手机");
        }
    }
  • 测试

    public class Test {
    ​
        public static void main(String[] args) {
    ​
            FolderPhone folderPhone = new FolderPhone(new XiaoMi());
            folderPhone.call();
        }
    }

    这样编写完后扩展性很好,想加入新的品牌或者样式只需要各编写一个类,通过桥接类进行关联调用即可。

组合模式

应用:依据树形结构来组合对象,用来表示部分以及整体层次,这样客户端只需要面对一致对象而无需考虑整体部分或者叶子节点的问题。

与桥接模式有什么不同?

桥接模式适用的是多个同级层次的组合,而组合模式适用于整体与部分的结构。

透明方式:

在 Component 中声明所有管理子对象的方法,包括 add 、remove 等,这样继承自 Component 的子类都具备了 add、remove 方法。对于外界来说叶节点和枝节点是透明的,它们具备完全一致的接口。

安全方式:

在 Component 中不声明 add 和 remove 等管理子对象的方法,或者进行默认实现并且抛出异常以确保客户端调用子类未重写该方法能抛出异常,这样叶节点就无需实现它(客户端即使调用也会因为调用的是超类的默认实现而抛出异常)只需在枝节点中实现管理子对象的方法即可。

模拟代码(使用安全方式)

  1. 创建一个组合组件类,所有需要组合的类都需要继承该抽象类
    方法可以进行实现,并且通过抛出异常来告诉调用者该类方法需要子类进行重写才可调用,并且也省去了抽象类中的抽象方法子类必须全数重写的麻烦,毕竟有些子类其实根本没必要去重写该方法。✅✅

    public abstract class OrganizationComponent {
        private String name;
        private String des;//说明
    ​
        protected void add(OrganizationComponent organizationComponent){
            throw new UnsupportedOperationException();
        }
    ​
        protected void remove(OrganizationComponent organizationComponent){
            throw new UnsupportedOperationException();
        }
    ​
        public OrganizationComponent(String name, String des) {
            this.name = name;
            this.des = des;
        }
    ​
        public String getName() {
            return name;
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    ​
        public String getDes() {
            return des;
        }
    ​
        public void setDes(String des) {
            this.des = des;
        }
    ​
        //print方法
        protected abstract void print();
    }
  2. 创建层次关系的三个类,大学,学院,系,学院聚合到大学之中,通过链表来增加删除,系聚合到学院中等

    //Composite,可以管理College
    public class University extends OrganizationComponent{
    ​
    List<OrganizationComponent> organizationComponents = new ArrayList<>();
    ​
        public University(String name, String des) {
            super(name, des);
        }
    ​
        @Override
        protected void add(OrganizationComponent organizationComponent) {
            organizationComponents.add(organizationComponent);
        }
    ​
        @Override
        protected void remove(OrganizationComponent organizationComponent) {
            organizationComponents.remove(organizationComponent);
        }
    ​
        @Override
        protected void print() {
            System.out.println(getName());
            for (OrganizationComponent organizationComponent : organizationComponents) {
                organizationComponent.print();
            }
    ​
        }
    }
    public class College extends OrganizationComponent{
        List<OrganizationComponent> organizationComponents = new ArrayList<>();
    ​
        public College(String name, String des) {
            super(name, des);
        }
    ​
        @Override
        protected void add(OrganizationComponent organizationComponent) {
            organizationComponents.add(organizationComponent);
        }
    ​
        @Override
        protected void remove(OrganizationComponent organizationComponent) {
            organizationComponents.remove(organizationComponent);
        }
    ​
        @Override
        protected void print() {
            System.out.println(getName());
            for (OrganizationComponent organizationComponent : organizationComponents) {
                organizationComponent.print();
            }
    ​
        }
    }
    public class Department extends OrganizationComponent{
    ​
        public Department(String name, String des) {
            super(name, des);
        }
    ​
        @Override
        protected void print() {
            System.out.println(getName());
        }
    }

装饰者模式

目的:动态得将功能附加到对象上,功能扩展方面更具有弹性,体现了ocp原则

代码模拟

  1. 创建Drink类,继承该类的即使被修饰者,创建Coffe类,作为各种Coffe的超类

    public class Coffee extends Drink{
        @Override
        public Double cost() {
            return super.getPrice();
        }
    }
    
    public abstract class Drink {
    ​
        public String des;//描述
    ​
        private Double price = 0d;
    ​
        public String getDes() {
            return des;
        }
    ​
        public void setDes(String des) {
            this.des = des;
        }
    ​
        public Double getPrice() {
            return price;
        }
    ​
        public void setPrice(Double price) {
            this.price = price;
        }
    ​
        //计算价格
        public abstract Double cost();
    }
  2. 编写一个类继承Coffee,这个类就是被修饰者

    public class LongBlack extends Coffee{
    ​
        public LongBlack() {
            setDes("longBlack");
            setPrice(10d);
        }
    }

  3. 编写装饰类,继承Drink,聚合被修饰者,价格计算和描述存在递归

    public class Decorator extends Drink{
    ​
        private Drink drink;
    ​
        public Decorator(Drink drink) {
            this.drink = drink;
        }
    ​
        @Override
        public Double cost() {
            return super.getPrice() + drink.cost();
        }
    ​
        @Override
        public String getDes() {
            return super.des + " " + super.getPrice() + "&&" + drink.getDes();
        }
    }
  4. 编写增强功能的类,继承Decorator类,在其中即可增强原有类的功能而不改变其本身的功能

    public class Chocolate extends Decorator{
        public Chocolate(Drink drink) {
            super(drink);
            setDes("巧克力");
            setPrice(5d);
        }
    }
  5. 测试

    public class CoffeeBar {
        public static void main(String[] args) {
            Drink drink = new LongBlack();
            System.out.println(drink.cost());
            System.out.println(drink.getDes());
    ​
            drink = new Milk(drink);
            System.out.println(drink.cost());
            System.out.println(drink.getDes());
    ​
            drink = new Chocolate(drink);
            System.out.println(drink.cost());
            System.out.println(drink.getDes());
        }
    }

这样的模式扩展性十分好,想要增加修饰物或者修饰对象,只需要增加一个对应类即可,通过修饰类即可自由组装被修饰者和修饰的对象。

外观模式

作用:

  1. 外观模式为子系统一组接口提供一致界面,此模式定义了一个高层接口,使得这一子系统更加容易使用

  2. 降低了客户端对子系统使用的复杂性

  3. 系统需要分层设计时,可以考虑使用该模式,在 MVC 架构中,C 层(Controller)就可以看作是外观类,Model 和 View 层通过 Controller 交互,减少了耦合。

代码模拟

就是聚合子系统的行为,向外部提供简单的方法,不做多赘述

public class DVDPlayer {
​
    private static DVDPlayer instance = new DVDPlayer();
​
    public static DVDPlayer getInstance(){
        return instance;
    }
​
    public void on(){
        System.out.println("DVD打开");
    }
​
    public void close(){
        System.out.println("DVD关闭");
    }
​
    public void play(){
        System.out.println("DVD播放");
    }
}
public class HomeFacade {
​
    private DVDPlayer dvdPlayer;
    private Popcorn popcorn;
    private Projector projector;
​
    public HomeFacade() {
        this.dvdPlayer = DVDPlayer.getInstance();
        this.popcorn = Popcorn.getInstance();
        this.projector = Projector.getInstance();
    }
​
    public void ready(){
        popcorn.on();
        dvdPlayer.on();
        projector.on();
    }
​
    public void play(){
        dvdPlayer.play();
    }
​
    public void close(){
        popcorn.close();
        dvdPlayer.close();
        projector.close();
    }
}

享元模式(将可复用的特点体现到极致)

两个要求

  1. 细粒度和共享对象

  2. 内部状态:对象共享出来的信息,存储在享元对象内且不随环境变化,可放入常量池

  3. 外部状态:指对象得以依赖的标记,可随环境变化,不可共享的状态

意义

  1. 对象状态大部分可以外部化可以使用该模式

  2. 大大减少了对象创建,降低了内存占用

  3. 将对象放入map集合中,使用唯一标识码取出对象即可。

  4. 享元模式提高了系统复杂度,需要分离外部和内部状态,外部状态有固化特性,不能随内部状态改变

  5. 常用场景就是需要缓存池的场景,比如数据库连接池,String常量池

代码模拟

  1. 创建网站抽象类,具体实现会继承该类

    public abstract class WebSite {
        public abstract void useWebSite(User user);
    }
  2. 创建具体实现类

    public class ConcreteWebSite extends WebSite{
    ​
        private String type;
    ​
        public ConcreteWebSite(String type) {
            this.type = type;
        }
    ​
        @Override
        public void useWebSite(User user) {
            System.out.println("网站发布形式为:" + type + " " + "使用者为:" + user.getName());
        }
    }
  3. 创建工厂类,在工厂类中创建map集合存放共享的对象,这样每次获取无需创建新的对象,直接从池中获取即可,大大减少了资源的浪费

    //根据需求返回具体网站
    public class WebSiteFactory {
    ​
        private HashMap<String, ConcreteWebSite> pool = new HashMap<>();
    ​
        public WebSite getWebSiteCateGory(String type) {
    ​
            if (!pool.containsKey(type)) {
                pool.put(type, new ConcreteWebSite(type));
            }
            return (WebSite) pool.get(type);
    ​
        }
    }
  4. 外部对象模拟:User 不会因内部状态改变而改变

    public class User {
        private String name;
    ​
        public User(String name) {
            this.name = name;
        }
    ​
        public String getName() {
            return name;
        }
    ​
        public void setName(String name) {
            this.name = name;
        }
    }
  5. 客户端使用

    public class Client {
        public static void main(String[] args) {
            WebSiteFactory webSiteFactory = new WebSiteFactory();
            WebSite webSite = webSiteFactory.getWebSiteCateGory("博客");
            webSite.useWebSite(new User("Rosmontiss"));
        }
    }

代理模式Proxy

目的:对代理对象的行为加以控制

静态代理:较为简单直接,聚合代理对象并实现与代理对象相同的方法,在方法体类通过聚合的该对象调用代理对象的方法,并且能在方法体内做额外的处理。
 

动态代理:🔴🟠🟡🟢🔵🟣🟤⚫⚪(底层通过反射实现)这里举得例子未jdk实现,还存在一种CGlib实现的动态代理

实现过程:

创建接口

public interface Emp {
    public int CountNum(int a,int b);
}
​

创建实现类

public class Rosmontiss implements Emp{
    @Override
    public int CountNum(int a, int b) {
        int sum = a +b;
        System.out.println("迷迭香~~" + sum);
        return sum;
    }
}

创建生成代理对象的核心类

public class EmpProxyProvider {
​
    /*
    代理的目标对象,并且实现了Emp接口
     */
    private Emp target_emp;
​
    //构造器传入需要代理的目标对象
    public EmpProxyProvider(Emp target_emp) {
        this.target_emp = target_emp;
    }
​
    //返回代理对象,运行类型会变为代理类
    public Emp getProxy() {
​
        //得到类加载器
        ClassLoader classLoader = target_emp.getClass().getClassLoader();
        //得到要代理的对象的实现的接口信息,底层是通过接口调用的
        Class<?>[] interfaces = target_emp.getClass().getInterfaces();
        //调用处理器,java基础中匿名对象的使用,InvocationHandler()是借口,不能实例化,只能通过匿名对象创建,匿名内部类
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                /*
                 proxy就是需要代理的对象
                 method就是代理对象调用的那个方法
                 args 就是方法中的参数
                */
                System.out.println("方法调用前....");
                //反射调用,返回值就是方法的返回值
                Object result = method.invoke(target_emp, args);
                System.out.println("方法调用后....");
                return result;
            }
        };
        //使用三件套生成对应代理对象
        //生成的对象会使用接口类型来接收
        Emp proxyInstance = (Emp) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxyInstance;
​
    }
}

测试类

public class ProxyTest {
    public static void main(String[] args) {
        //创建类传入需要代理的对象
        EmpProxyProvider empProxyProvider = new EmpProxyProvider(new Rosmontiss());
        //获取代理对象
        Emp proxy = empProxyProvider.getProxy();
        //使用代理对象调用方法
        int i = proxy.CountNum(1, 2);
        System.out.println(i);
    }
}

动态代理大幅度节省了代码量,编写完成后他将对代理类的每个方法都进行影响,而静态代理还需要重写每个方法,再进行操作。

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到