设计模式及七大原则

本文是大一学生的学习笔记,详述了23种设计模式,特别是单例模式的多种实现方式和工厂模式的分类。此外,文章还深入探讨了面向对象设计的七大原则,包括开闭原则、里氏替换原则等,旨在提高编程和设计能力。
摘要由CSDN通过智能技术生成

大一菜鸡的个人笔记,欢迎指点和交流

23种设计模式

学习设计模式的意义

​ 设计模式的本质是面向对象设计原则的实际应用,是对类的封装性、继承性、和多态性以及类的关联关系和组合关系的充分理解。

​ 优点:

  • 可以提高程序员的思维能力,编程能力和设计能力。
  • 使程序设计更加标准化,代码编程更加工程化,使软件开发效率大大提高,从而缩短软件的开发周期。
  • 使设计的代码可重用性高、可读性强、可靠性高、灵活性好、可维护性强。

OOP七大原则

开闭原则

对扩展开发 对修改关闭

里氏替换原则

继承必须确保超类所拥有的性质在子类中仍然成立

意思是不重写父类的方法 子类自己写新方法

因为重写的复用性不好

依赖倒置原则

要面向接口编程,不要面向实现编程。

抽象不依赖细节,而细节应该依赖抽象。

单一职责原则

控制类的粒度大小,将对象解耦、提高其内聚性。

接口隔离原则

要为各个类建立他们需要的专用接口

迪米特法则

只与你的直接朋友交谈,不和陌生人讲话

合成复用原则

尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来来实现。

继承和组合的区别

组合:在子类中写自己方法的独特实现,再把子类对象传到其他类中,使传入不同的对象实现不同的功能。

各模式的分类

image-20200712182142316

创建型:省去new 更好地创建对象

结构型:实现松耦合

单例模式

单例模式的应用场景(作用)

单例模式:让一个类只能创建一个实例,实现对象的唯一性。当对象的创建比较消耗资源,且我们只需要一个这样的对象,就可以使用单例模式。

饿汉式单例

一上来直接加载(使用static) 可能会浪费资源

饿汉式是线程安全的

因为在加载类的时候就已经加载了对象 类在整个生命周期中只会被加载一次 因此只会创建一个实例对象

image-20200710200556899

懒汉式单例

普通版(线程不安全)

image-20200710211739989

不使用锁的问题:

image-20200710213046174

使用锁:

image-20200710213222879

虽然安全了 但同步方法本身导致很大的性能消耗,并且加锁只需要在第一次初始化的时候用到,之后的调用都没必要加锁。

错误版DCL懒汉(线程安全)

使用双重检测的意义是节省资源

先判断对象是否已经被初始化,再决定要不要加锁。

只用在创建时加锁 节省了资源

第一次非空检测是防止已经初始化后,获取锁,等待锁的资源浪费。避免不必要的阻塞

第二次非空检测是防止多个线程同时通过了第一次检测,此时还未初始化,第二个线程阻塞等待,获取锁之后如果对象不为空且创建则违反单例原则。

image-20200710211521227

这个版本有隐患:指令重排会导致出错

创建新对象不是一个原子性操作(编译器会进行指令重排)

1.分配内存空间

2.执行构造方法,初始化对象

3.把这个对象指向这个空间

可能顺序为123 也可能 132(先占用空间,再把对象放进去)

对象指向空间后释放了锁?

导致高并发时第二个线程可能会访问到未被初始化的对象

image-20200710224822002

正确版DCL懒汉

避免了指令重排

image-20200710224935940

静态内部类

public class Singleton {

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    private Singleton() {
        
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}

反射破坏单例

image-20200710225527342

输出的两个hashcode不相同 单例被破坏

以上的单例模式虽然可以做到线程安全 但是仍可能被反射破坏单例 我们需要用枚举来解决这个问题

枚举

image-20200711124922495

image-20200711132801911

尝试使用反射破解枚举时需要注意

如果把枚举中当作无参构造 直接填null IDEA会提示该类没有无参构造器(使用JAD反编译字节码可知)

image-20200711124744655

工厂模式

简单工厂模式(静态工厂模式)

在原来的类中加代码,并且使用static。违反了闭包原则。

工厂方法模式

每种类型做一个新的工厂实现接口工厂 代码量大 麻烦

抽象工厂模式

创建工厂的工厂

使用场景:在需求及其稳定的时候抽象工厂模式非常稳定

优点:具体产品在应用层的代码隔离,无需关心创建的细节。

将一系列的产品统一到一起创建

缺点:规定了所有可能被创建的产品集合,扩展性不强。

增加了系统的抽象性和理解难度。

image-20200712175947487

image-20200712180054940

image-20200712180137316

image-20200712180506918

建造者模式

优点和使用场景

创建型模式的其中之一。

定义:将一个复杂对象的构建和他的表示分离,使得同样的构建过程可以创建不同的表示。

主要作用在用户不知道对象的建造过程和细节的情况下就可以创建复杂的对象。

image-20200712210501229

image-20200712210513110

指挥者实现

image-20200712182619485

image-20200712183243957

image-20200712183514563

image-20200712183724933

image-20200712183909151

静态内部类实现

image-20200712205114433

image-20200712205246663

image-20200712205801696

image-20200712210127147

原型模式

场景

浅克隆

一个对象的创建非常麻烦 使用原型模式克隆一个相同的

image-20200712212825658

image-20200712213128870

是用的是浅克隆 一但date重新赋值 两个对象的date都会改变 这是不好的

image-20200712213625363

深克隆

image-20200712214314078

适配者模式

将一个类的接口转换成客户希望的另外一个接口,使得原本不兼容而不能一起工作的类一起工作。

image-20200712234720989

image-20200713105116647

image-20200713104517919

image-20200713104854834

桥接模式

桥接模式的优劣

image-20200713111439578

原本:

image-20200713111238085

使用桥接模式后:

image-20200713111301118

image-20200713111642792

image-20200713111658904

image-20200713110712581

image-20200713110940943

image-20200713111049722

静态代理模式

意义

使真实角色的任务更加纯粹。

代理角色实现了任务的分工

业务扩展的时候 方便集中管理

缺点

代码量多。

中介的意义

中介的意义就是实现附属功能,而不只是租房。

中介起到了:带看很多房子,协调两方沟通,签合同。。。

image-20200713114537837

image-20200713115118646

image-20200713115731654

image-20200713115934573

代理实现功能增强

符合闭包原则

image-20200713122038247

动态代理模式

意义

使真实角色的任务更加纯粹。

代理角色实现了任务的分工

业务扩展的时候 方便集中管理

一个动态代理类可以代理多个类(方便添加功能)

简介

动态代理和静态代理角色一样

动态代理的代理类是动态生成的,不是我们直接写好的

动态代理分为两大类:基于接口的动态代理,基于类的动态代理

  • 基于接口:JDK动态代理
  • 基于类:cglib

需要了解两个类:Proxy、invokatio

/**
 * @author:zmc
 * @function:
 * @date: 2020/7/13 15:37
 */
public interface Rent {
    public void rent();
}
/**
 * @author:zmc
 * @function:
 * @date: 2020/7/13 15:38
 */
public class Host implements Rent{

    @Override
    public void rent() {
        System.out.println("房东要出租房子");
    }
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author:zmc
 * @function:
 * @date: 2020/7/13 15:40
 */
//用这个类生成代理类
public class ProxyInvocationHandler implements InvocationHandler {

    //被代理的接口
    private Rent rent;

    public void setRent(Rent rent) {
        this.rent = rent;
    }

    //得到代理类
    public Object getProxy(){
      return Proxy.newProxyInstance(this.getClass().getClassLoader(),
                rent.getClass().getInterfaces(),this);
    }
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        seeHouse();
        //动态代理的本质,就是使用反射机制实现
        Object result = method.invoke(rent, objects);
        fare();
        return null;
    }
    public void seeHouse(){
        System.out.println("中介带看房子");
    }
    public void fare(){
        System.out.println("收中介费");
    }
}
/**
 * @author:zmc
 * @function:
 * @date: 2020/7/13 16:06
 */
public class Client {
    public static void main(String[] args) {
        //真实角色
        Host host = new Host();
        //代理角色
        ProxyInvocationHandler pih = new ProxyInvocationHandler();
        //通过调用程序处理角色来处理我们调用的接口对象
        pih.setRent(host);
        //这里的proxy就是动态生成的
        Rent proxy = (Rent)pih.getProxy();
        proxy.rent();
    }
}

在这里插å¥å›¾ç‰‡æè¿°

​ 动态代理模式的优势就是可以利用反射对方法进行拓展,不需要像静态代理模式那样对以前的代码进行修改,动态代理模式符合闭包原则。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值