设计模式(残)

OOP七大原则(OOP:面向对象编程)

  • 开闭原则:对扩展开发,对修改关闭
  • 里氏替换原则:继承必须确保父类所拥有的性质在子类中仍然成立(尽量少重写父类的方法)
  • 依赖倒置原则:要面向接口编程,不要面向实现编程
  • 单一职责原则:控制类的粒度大小,将对象解耦,提高其内聚性(一个方法不要处理太多东西)
  • 接口隔离原则:要为各个类建立它们需要的专用接口
  • 迪米特法则:只与你的直接朋友交谈,不跟“陌生人”说话
  • 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现

单例模式(创建型模式)

核心作用:保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

常见场景:

  • windows的任务管理器
  • 项目中,读取配置文件的类,一般也只有一个对象,没必要每次都去new对象读取
  • 数据库的连接池的设计也会采用单例模式
  • 在spring中,每个bean默认就是单例的

工厂模式(创建型模式)

作用:实现了创建者和调用者的分离

核心本质:

  • 实例化对象不适用new,用工厂方法代替
  • 将选择实现类,创建对象统一管理和控制。从而将调用者跟我们的实现类解耦

分为简单工厂、方法工厂和抽象工厂(工厂的工厂)

建造者模式(创建型模式)

作用:实现了创建者和调用者的分离,将不同的模块组合起来,建造一个复杂的对象

建造者模式中,有如下四种角色:
1.抽象建造者(Builder):该绝是用于规范产品的各个组成部分,并进行抽象,一般独立于应用程序的逻辑。
2.具体建造者(Concrete Builder):该角色实现抽象建造者中定义的所有方法,并且返回一个组件好的产品实例。
3.产品(Product):该角色是建造者中的复杂对象,一个系统中会有多个产品类,这些产品类不一定有共同的接口,可以是完成不相关的。
4.导演者(Director):该角色负责安排已有模块的顺序,然后告诉Builder开始建造

个人认为:产品(Product)相当于pojo层中的实体类;抽象建造者(Builder)相当于service层的接口;具体建造者(Concrete Builder)相当于service层的实现类;导演者(Director)相当于controller层的控制器

原型模式(创建型模式)

原型模式用于创建重复对象并且保证性能,它属于是一种创建型的设计模式,提供了一种创建对象的最佳方式。

java实现原型模式

  • 1.实现一个接口 Cloneable
  • 2.重写一个方法 clone()

但是如果要克隆的类中有引用的数据类型,那么导致了浅克隆(指向同一个引用数据类型),即修改一个对象中引用的数据类型,另一个克隆对象中的数据类型也会发生改变。

扫描二维码关注公众号,回复: 13272187 查看本文章

所以要实现深克隆(指向不同的引用数据类型),即修改一个对象中引用的数据类型,另一个克隆对象中的数据类型不会发生改变。要实现深克隆,则要对clone()方法进行重写

private Date createTime;

@Override
protected Object clone() throws CloneNotSupportedException {
    
    
    Object obj = super.clone();

    // 实现深克隆
    Video v = (Video) obj;

    //将这个对象的属性也进行克隆
    v.createTime = (Date) this.createTime.clone();

    return obj;
}

在实际开发中,更经常使用到的是深克隆,即对象中的对象也被克隆出来

适配器模式(结构型模式)

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

类适配器:通过继承要被适配的类实现

对象适配器:不继承,而通过创建要被适配的类的实例来实现,就像是service层中创建mapper的实例

建议使用对象适配器

对象适配器的优点:

  • 一个对象适配器可以把多个不同的适配者适配到同一个目标
  • 可以适配一个适配者的子类,由于适配器和适配者之间是关联关系,根据“里氏替换原则”,适配者的子类也可通过改适配器进行适配

桥接模式(结构型模式)

优势:

  • 桥接模式偶尔类似于多继承方案,但是多继承方案违背了类的单一职责原则,复用性比较差,类的个数也非常多,桥接模式是比多继承方案更好的解决方法,极大的减少了子类的个数,从而降低管理和维护的成本
  • 桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。符合开闭原则,就像一座桥,可以把两个变化的维度连接起来

劣势:

  • 桥接模式的引入会增加系统的理解与设计难度,由于聚合关联关系建立在抽象层,要求开发者针对抽象进行设计与编程
  • 桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性

使用场景

  • Java语言通过Java虚拟机实现了平台的无关性
  • JDBC驱动程序也是桥接模式的应用

代理模式(结构型模式)

静态代理

角色分析:

  • 抽象角色:一般会使用接口或者抽象类来解决
  • 真实角色:被代理的角色
  • 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作
  • 客户:访问代理对象的人

代码

  1. 接口

    //租房
    public interface Rent {
          
          
        public void rent();
    
    }
    
  2. 真实角色

    //房东
    public class Host implements Rent{
          
          
        
        public void rent() {
          
          
            System.out.println("房东要出租房子");
        }
    }
    
  3. 代理角色

    public class Proxy implements Rent{
          
          
        private Host host;
    
        public Proxy(Host host) {
          
          
            this.host = host;
        }
    
        public Proxy() {
          
          
        }
    
    
        public void rent() {
          
          
            host.rent();
            seeHouse();
            fare();
            hetong();
        }
        //看房
        public void seeHouse(){
          
          
            System.out.println("中介带你看房");
        }
        //收中介费
        public void fare(){
          
          
            System.out.println("收中介费");
        }
        //签合同
        public void hetong(){
          
          
            System.out.println("签合同");
        }
    
    }
    
  4. 客户端访问代理角色

public class Client {
    
    
    public static void main(String[] args) {
    
    
        Host host = new Host();
        //代理,代理一般会有一些附属操作
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,不用去关注一些公共的业务
  • 公共也就交给了代理角色,实现了业务的分工
  • 公共业务发生扩展的时候,方便集中管理

缺点:

  • 一个真实角色就会产生一个代理角色,代码量翻倍,开发效率会变低

动态代理

  • 动态代理和静态代理角色一样
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理
    • 基于接口-JDK动态代理【我们在这里使用】
    • 基于类:cglib
    • java字节码实现:javassist

核心 : InvocationHandler:调用处理程序 和 Proxy:代理

动态代理的好处:

除了有静态代理的所有好处外,还有

  • 一个动态代理类代理的是一个接口,一般是对应的一类业务
  • 一个动态代理类可以代理多个类,只要是实现了同一个接口

UserService接口

public interface UserService {
    
    
     void add();
     void delete();
     void update();
     void query();
}

UserServiceImpl实现类

public class UserServiceImpl implements UserService{
    
    
    @Override
    public void add() {
    
    
        System.out.println("增加了一个用户");
    }

    @Override
    public void delete() {
    
    
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
    
    
        System.out.println("修改了一个用户");
    }

    @Override
    public void query() {
    
    
        System.out.println("查询了一个用户");
    }
}

代理类:

//我们会用这个类,自动生成代理类
public class ProxyInvocationHandler implements InvocationHandler {
    
    
    //被代理的接口
    private Object target;

    public void setTarget(Object target) {
    
    
        this.target = target;
    }
    //生成得到代理类
    public Object getProxy(){
    
    

        return Proxy.newProxyInstance(this.getClass().getClassLoader()
                ,target.getClass().getInterfaces(),this);
    }


    //处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        log(method.getName());
        //动态代理的本质,就是使用反射机制实现
        Object result = method.invoke(target, args);

        return result;
    }

    public void log(String msg){
    
    
        System.out.println("执行了"+msg+"方法");
    }

}

测试

public class Client {
    
    
    public static void main(String[] args) {
    
    
        //真实角色
        UserServiceImpl userService = new UserServiceImpl();

        //代理角色,不存在
        ProxyInvocationHandler pih = new ProxyInvocationHandler();

        pih.setTarget(userService);//设置要代理的对象

        //动态生成代理类
        UserService proxy = (UserService) pih.getProxy();

        proxy.add();

    }

}

结果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_45598422/article/details/120591810