设计模式总结

概述

什么是设计模式

设计模式代表了面向对象编程的最佳实践,使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。

设计模式六大原则

  1. 开闭原则:对扩展开放、对修改关闭
  2. 里氏替换原则:子类可以替换父类
  3. 单一职责原则:(接口隔离原则)一个类只做一件事
  4. 依赖倒置原则:面向接口编程,依赖抽象而不依赖于具体
  5. 迪米特法则:最少知道原则,一个类应当尽量少的与其他类相互作用
  6. 接口隔离原则:使用多个专门的接口,而不使用单一的总接口,客户端不应依赖它不需要的接口

设计模式都有哪些?

共有23种设计模式
分为三大类:创建型、结构型、行为型

创建型

  • 工厂方法模式:为每一类对象建立工厂,将对象交由工厂创建,客户端只和工厂打交道。
  • 抽象工厂模式:为每一类工厂提取出抽象接口,使得新增工厂、替换工厂变得非常容易。
  • 建造者模式:用于创建构造过程稳定的对象,不同的 Builder 可以定义不同的配置。
  • 单例模式:全局使用同一个对象,分为饿汉式和懒汉式。懒汉式有双检锁和内部类两种实现方式。
  • 原型模式:为一个类定义 clone 方法,使得创建相同的对象更方便。工厂方法模式

结构型

  • 适配器模式:用于有相关性但不兼容的接口
  • 桥接模式:用于同等级的接口互相组合
  • 组合模式:用于整体与部分的结构
  • 装饰模式
  • 外观模式
  • 享元模式
  • 代理模式

行为型

  • 责任链模式
  • 命令模式
  • 解释器模式
  • 迭代器模式
  • 中介者模式
  • 备忘录模式
  • 观察者模式
  • 状态模式
  • 策略模式
  • 模板方法模式
  • 访问者模式

创建型

工厂方法模式

定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
在这里插入图片描述

  • 产品 (Product) 将会对接口进行声明。 对于所有由创建者及其子类构建的对象, 这些接口都是通用的。

  • 具体产品 (Concrete Products) 是产品接口的不同实现。

  • 创建者 (Creator) 类声明返回产品对象的工厂方法。 该方法的返回对象类型必须与产品接口相匹配。

    你可以将工厂方法声明为抽象方法, 强制要求每个子类以不同方式实现该方法。 或者, 你也可以在基础工厂方法中返回默认产品类型。

    注意, 尽管它的名字是创建者, 但他最主要的职责并不是创建产品。 一般来说, 创建者类包含一些与产品相关的核心业务逻辑。 工厂方法将这些逻辑处理从具体产品类中分离出来。 打个比方, 大型软件开发公司拥有程序员培训部门。 但是, 这些公司的主要工作还是编写代码, 而非生产程序员。

  • 具体创建者 (Concrete Creators) 将会重写基础工厂方法, 使其返回不同类型的产品。

注意, 并不一定每次调用工厂方法都会创建新的实例。 工厂方法也可以返回缓存、 对象池或其他来源的已有对象。

以水果工厂为例

  • 产品接口:Fruit
  • 具体产品:Apple、Pear实现Fruit接口

苹果工厂

class AppleFactory{
    public Fruit creat(){
        return new Apple();
    }    
}

梨子工厂

class PearFactory{
    public Fruit creat(){
        return new Pear();
    }
}

调用者

class User{
    public void eat(){
        AppleFactory appleFactory = new AppleFactory();
        Fruit apple = appleFactory.creat();
        PearFactory pearFactory = new PearFactory();
        Fruit pear = pearFactory.creat();
    }
}

抽象工厂模式

在抽象工厂模式中,接口是负责创建一个相关对象的工厂,不需要显式指定它们的类。每个生成的工厂都能按照工厂模式提供对象。
在这里插入图片描述
将工厂方法模式优化

提取工厂接口

public interface IFactory{
    Fruit creat();
}

苹果工厂和梨子工厂都实现此接口

class AppleFactory implements IFactory{
	@Override
    public Fruit creat(){
        return new Apple();
    }    
}
class PearFactory implements IFactory{
	@Override
    public Fruit creat(){
        return new Pear();
    }
}

调用者

class User{
    public void eat(){
        IFactory factory1 = new AppleFactory();
        Fruit apple = factory1.creat();
        IFactory factory1 = new PearFactory();
        Fruit pear = factory2.creat();
    }
}

单例模式

概述

保证一个类仅有一个实例,并提供一个访问它的全局访问点。关键在于私有的无参构造器

使用场景

  1. java.lang.Runtime#getRuntime(),返回一个单例的当前时间
  2. Spring
    • Spring的Bean 默认情况下是单例的
    • Spring 框架对单例的支持是采用单例注册表的方式进行实现的

在这里插入图片描述

饿汉式

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }

} 

类加载时就初始化,因为类加载过程是加了同步锁的,因此可以保证只初始化一次,不会产生多个实例。

  • 无锁,执行效率高
  • 类加载时就初始化。浪费内存

双重校验懒汉式

延迟加载,线程安全

public class Singleton {
	//防止代码指令重排
    private volatile static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
    	
        if (instance == null) {
        	//多个线程都需要获取锁,效率低下,所以再次判空
            synchronized (Singleton.class) {
            	//多个线程调用判空则线程不安全,所以需要加锁
                if (instance == null) {
                    instance = new Singleton();
                    /*上述新建一个对象有三个步骤
                     1.分配对象的内存空间
                     2.初始化对象
                     3.设置instance指向刚分配的内存地址
                     重排序后2 3可能互换位置
                     线程1执行1 3,未执行2时切换为线程2,
                     线程2判空时instance不为null,拿到一个未初始化的空间造成错误
                     */
                }
            }
        }

        return instance;
    }

} 

静态内部类式

延迟加载、线程安全

public class Singleton {

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

    private Singleton() {}

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

} 

类加载时不会加载内部类。内部类在使用时才会被加载。当访问内部类的静态字段时,将会初始化内部类,这时才new出Singleton对象

注册表登记式

在静态代码块中初始化并将类名+实例存入哈希表,根据指定类的名称获取实例

public class Singleton{
	private static  Map<String, Singleton> = new HashMap<String, Singleton>();
	static{
		Singleton singleton = new Singleton();
		map.put(singleton.getClass().getName(), singleton);
	}
	private Singleton() {}

    public static Singleton getInstance(String name) {
        return map.get(name);
    }
	
}

枚举式

public enum Singleton {

    INSTANCE;

    public void whateverMethod() {}

}

结构型

装饰器模式

向一个现有的对象增强原有功能或添加新的功能,同时又不改变其结构

java.io.InputStream、 Output­Stream、 Reader 和 Writer 的所有代码都有以自身类型的对象作为参数的构造函数。
在这里插入图片描述

在这里插入图片描述
部件 (Component) 声明封装器和被封装对象的公用接口。

具体部件 (Concrete Component) 类是被封装对象所属的类。 它定义了基础行为, 但装饰类可以改变这些行为。

基础装饰 (Base Decorator) 类拥有一个指向被封装对象的引用成员变量。 该变量的类型应当被声明为通用部件接口, 这样它就可以引用具体的部件和装饰。 装饰基类会将所有操作委派给被封装的对象。

具体装饰类 (Concrete Decorators) 定义了可动态添加到部件的额外行为。 具体装饰类会重写装饰基类的方法, 并在调用父类方法之前或之后进行额外的行为。

客户端 (Client) 可以使用多层装饰来封装部件, 只要它能使用通用接口与所有对象互动即可。

代理模式

代理控制着对于原对象的访问, 并允许在将请求提交给对象前后进行一些处理。
在这里插入图片描述

  1. 服务接口 (Service Interface) 声明了服务接口。 代理必须遵循该接口才能伪装成服务对象。
  2. 服务 (Service) 类提供了一些实用的业务逻辑。
  3. 代理 (Proxy) 类包含一个指向服务对象的引用成员变量。 代理完成其任务 (例如延迟初始化、 记录日志、 访问控制和缓存等) 后会将请求传递给服务对象。 通常情况下, 代理会对其服务对象的整个生命周期进行管理。
  4. 客户端 (Client) 能通过同一接口与服务或代理进行交互, 所以你可在一切需要服务对象的代码中使用代理。

静态代理

例子:在网络请求前后,打印一些信息
网络请求接口:

interface IHttp{
    void request(String sendData);
    void onSuccess(String receivedData);
}

HTTP 请求工具类

class HttpUtil implements IHttp{

    @Override
    public void request(String sendData) {
        System.out.println("网络请求中");
    }

    @Override
    public void onSuccess(String receivedData) {
        System.out.println("网络请求完成");
    }
}

HTTP 代理类

class HttpProxy implements IHttp{
    private final HttpUtil httpUtil;

    public HttpProxy(HttpUtil httpUtil) {
        this.httpUtil = httpUtil;
    }

    @Override
    public void request(String sendData) {
        System.out.println("发送数据:" + sendData);
        httpUtil.request(sendData);
    }

    @Override
    public void onSuccess(String receivedData) {
        System.out.println("收到数据:" + receivedData);
        httpUtil.onSuccess(receivedData);
    }
}

客户端

class Client{
    @Test
    public void test(){
        HttpUtil httpUtil = new HttpUtil();
        HttpProxy httpProxy = new HttpProxy(httpUtil);
        httpProxy.request("send data");
        httpProxy.onSuccess("received result");
    }
}

结果为

发送数据:send data
网络请求中
收到数据:received result
网络请求完成

动态代理

分为JDK动态代理和CGlib动态代理

  • JDK动态代理通过反射,生成一个实现代理接口的代理类,重写方法,进行功能增强
  • CGlib动态代理通过字节码,对Class文件修改生成子类

代理类

class HttpProxy implements InvocationHandler {
    private  HttpUtil httpUtil;

    public IHttp getInstance(HttpUtil httpUtil) {
        this.httpUtil = httpUtil;
        //Proxy.newProxyInstance()通过被代理类的类加载器、接口和代理类,返回被代理类的实例
        //被代理类的任何方法都要经过下面的invoke()
        return (IHttp) Proxy.newProxyInstance(httpUtil.getClass().getClassLoader(),
                                             httpUtil.getClass().getInterfaces(), this);
    }


    //调用httpUtil的任意方法时,都要通过这个方法调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        if(method.getName().equals("request")){
            System.out.println("发送数据:" + args[0]);
            result = method.invoke(httpUtil, args);
        }
        else if(method.getName().equals("onSuccess")){
            System.out.println("收到数据:" + args[0]);
            result = method.invoke(httpUtil, args);
        }
        return result;
    }
}

客户端

class Client{
    //@Test
    public void test(){
        HttpUtil httpUtil = new HttpUtil();
        IHttp proxy = new HttpProxy().getInstance(httpUtil);
        proxy.request("request data");
        proxy.onSuccess("received result");
    }
}

行为型

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值