二十三种设计模式(待更)

这篇具有很好参考价值的文章主要介绍了二十三种设计模式(待更)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

二十三种设计模式

资料来源于老师讲解以及大佬的设计模式仓库 zhengqingya

结构型

将对象和类按某种布局组成更大的结构,并同时保持结构的灵活和⾼效。

1.适配器

二十三种设计模式(待更)

适配器就是将原先无法直接使用的某个接口或者类通过适配器模式转换为可以使用的接口或者类。将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。适配器模式的好处就是可以再不改变原有代码的基础之上也可以实现相关的功能。

角色:

  1. 目标接口(Target):客户所期待的接口。可以是具体类 或 抽象的类,也可以是接口
  2. 需要适配的类(Adaptee)
  3. 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口

区别:

  • 类适配器:单继承,一次最多只能适配一个适配者类
  • 对象适配器:可以把多个不同的适配者适配到同一个目标

适配器模式分为:类适配器、对象适配器、接口适配器。

tips: 推荐使用对象适配器

二十三种设计模式(待更)

二十三种设计模式(待更)

二十三种设计模式(待更)

1.1 接口适配器

接口中有很多的抽象方法,但是再使用接口的时候我们只关注其中某一个方法,其他方法我们不需要,但是因为接口的特性,如果我们要使用这个接口,必须重写接口中所有方法。

创建一个接口的适配器,接口的适配器是一个Java类,适配器需要将接口的所有抽象方法给重写了,但是重写之后只做空实现,后期如果使用接口,只需要继承适配器类。

Java中GUI中有很多接口适配器,MouseAdpter

代码示例:

package com.xsuek.adpter;
/**
 * 二十三种设计模式之一,适配器模式
 * 在某些情况下Java类只想重写使用接口中的某个方法  而不是所有的抽象方法
 * 但是因为接口的特性  子类实现接口  必须重写所有方法
 * 
 * 适配器模式:一个适配类由这个适配类是实现接口,并且重写了所有的方法,但是所有的方法都是空实现。
 * 如果某个类想要去重写使用接口中的某个方法,就不要直接实现接口,而是继承适配器类。
 * @author lenovo
 *
 */
public interface MouseInter {
	void mouseClick();
	int mouseMove();
	String mouseEnter();
	void mouseleave();
}
class MouseAdpter implements MouseInter{
	@Override
	public void mouseClick() {			
	}
	@Override
	public int mouseMove() {
		return 0;		
	}
	@Override
	public String mouseEnter() {		return null;		
	}
	@Override
	public void mouseleave() {
        // TODO Auto-generated method stub		
	}		
}
class Button extends MouseAdpter{		
}
1.2 类适配器

有一个电压220V实现手机充电的效果电源适配器将220V的电压转换成为可以充电的东西。

让适配器类继承被适配器

package com.sxuek.designmodel;

public class U {
	public void out() {
		System.out.println("220V");
	}
}
interface Chongdian{
	void chongdian();
}
class ChrageAapter extends U implements Chongdian{
	@Override
	public void chongdian(){
			out();
			//xxxxxxx
	}
}
1.3 对象适配器

让被适配的类称为适配器的一个属性。

代码示例:

public class AC220 {
    public int outputAC220V() {
        int output = 220;
        System.out.println("输出电压:" + output + "V");
        return output;
    }
}
public interface DC5 {
    int outputDC5V();
}
public class PowerAdapter implements DC5 {

    private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int outputDC5V() {
        int adapterInput = ac220.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("输入AC" + adapterInput + "输出DC" + adapterOutput);
        return adapterOutput;
    }

}
public class Test {
    public static void main(String[] args) {
        DC5 adapter = new PowerAdapter(new AC220());

        adapter.outputDC5V();
    }
}

2.代理模式

为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

AOP核心:代理模式

在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。

通过引入一个新的对象(如小图片和远程代理 对象)来实现对真实对象的操作或者将新的对 象作为真实对象的一个替身,这种实现机制即 为代理模式,通过引入代理对象来间接访问一 个对象,这就是代理模式的模式动机。

2.1 模式结构
  1. Subject: 抽象主题角色
  2. Proxy: 代理主题角色
  3. RealSubject: 真实主题角色

二十三种设计模式(待更)

二十三种设计模式(待更)

2.1.1 静态代理

以租房为例,我们一般用租房软件、找中介或者找房东。这里的中介就是代理者。

首先定义一个提供了租房方法的接口。

public interface IRentHouse {
    void rentHouse();
}

定义租房的实现类

public class RentHouse implements IRentHouse {
    @Override
    public void rentHouse() {
        System.out.println("租了一间房子。。。");
    }
}

我要租房,房源都在中介手中,所以找中介

public class IntermediaryProxy implements IRentHouse {

    private IRentHouse rentHouse;

    public IntermediaryProxy(IRentHouse irentHouse){
        rentHouse = irentHouse;
    }

    @Override
    public void rentHouse() {
        System.out.println("交中介费");
        rentHouse.rentHouse();
        System.out.println("中介负责维修管理");
    }
}

这里中介也实现了租房的接口。

再main方法中测试

public class Main {

    public static void main(String[] args){
        //定义租房
        IRentHouse rentHouse = new RentHouse();
        //定义中介
        IRentHouse intermediary = new IntermediaryProxy(rentHouse);
        //中介租房
        intermediary.rentHouse();
    }
}

返回信息

交中介费
租了一间房子。。。
中介负责维修管理

这就是静态代理,因为中介这个代理类已经事先写好了,只负责代理租房业务

2.1.2.强制代理

如果我们直接找房东要租房,房东会说我把房子委托给中介了,你找中介去租吧。这样我们就又要交一部分中介费了,真坑。

来看代码如何实现,定义一个租房接口,增加一个方法。

public interface IRentHouse {
    void rentHouse();
    IRentHouse getProxy();
}

这时中介的方法也稍微做一下修改

public class IntermediaryProxy implements IRentHouse {

    private IRentHouse rentHouse;

    public IntermediaryProxy(IRentHouse irentHouse){
        rentHouse = irentHouse;
    }

    @Override
    public void rentHouse() {
        rentHouse.rentHouse();
    }

    @Override
    public IRentHouse getProxy() {
        return this;
    }
}

其中的getProxy()方法返回中介的代理类对象

我们再来看房东是如何实现租房:

public class LandLord implements IRentHouse {

    private IRentHouse iRentHouse = null;

    @Override
    public void rentHouse() {
        if (isProxy()){
            System.out.println("租了一间房子。。。");
        }else {
            System.out.println("请找中介");
        }
    }

    @Override
    public IRentHouse getProxy() {
        iRentHouse = new IntermediaryProxy(this);
        return iRentHouse;
    }

    /**
     * 校验是否是代理访问
     * @return
     */
    private boolean isProxy(){
        if(this.iRentHouse == null){
            return false;
        }else{
            return true;
        }
    }
}

房东的getProxy方法返回的是代理类,然后判断租房方法的调用者是否是中介,不是中介就不租房。

main方法测试:

public static void main(String[] args){

        IRentHouse iRentHosue = new LandLord();
        //租客找房东租房
        iRentHouse.rentHouse();
        //找中介租房
        IRentHouse rentHouse = iRentHouse.getProxy();
        rentHouse.rentHouse();


    }

}
请找中介
租了一间房子。。。

看,这样就是强制你使用代理,如果不是代理就没法访问。

2.1.3 动态代理

我们知道现在的中介不仅仅是有租房业务,同时还有卖房、家政、维修等得业务,只是我们就不能对每一个业务都增加一个代理,就要提供通用的代理方法,这就要通过动态代理来实现了。

中介的代理方法做了一下修改

public class IntermediaryProxy implements InvocationHandler {


    private Object obj;

    public IntermediaryProxy(Object object){
        obj = object;
    }

    /**
     * 调用被代理的方法
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = method.invoke(this.obj, args);
        return result;
    }

}

在这里实现InvocationHandler接口,此接口是JDK提供的动态代理接口,对被代理的方法提供代理。其中invoke方法是接口InvocationHandler定义必须实现的, 它完成对真实方法的调用。动态代理是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理就会实现接口下所有的方法。通过 InvocationHandler接口, 所有方法都由该Handler来进行处理, 即所有被代理的方法都由 InvocationHandler接管实际的处理任务。

这里增加一个卖房的业务,代码和租房代码类似。

main方法测试:

public static void main(String[] args){

    IRentHouse rentHouse = new RentHouse();
    //定义一个handler
    InvocationHandler handler = new IntermediaryProxy(rentHouse);
    //获得类的class loader
    ClassLoader cl = rentHouse.getClass().getClassLoader();
    //动态产生一个代理者
    IRentHouse proxy = (IRentHouse) Proxy.newProxyInstance(cl, new Class[]{IRentHouse.class}, handler);
    proxy.rentHouse();

    ISellHouse sellHouse = new SellHouse();
    InvocationHandler handler1 = new IntermediaryProxy(sellHouse);
    ClassLoader classLoader = sellHouse.getClass().getClassLoader();
    ISellHouse proxy1 = (ISellHouse) Proxy.newProxyInstance(classLoader, new Class[]{ISellHouse.class}, handler1);
    proxy1.sellHouse();

}
租了一间房子。。。
买了一间房子。。。

在main方法中我们用到了Proxy这个类的方法,

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

loder:类加载器,interfaces:代码要用来代理的接口, h:一个 InvocationHandler 对象 。

InvocationHandler 是一个接口,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

因为,Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

总结:

1、静态代理,代理类需要自己编写代码写成。

2、动态代理,代理类通过 Proxy.newInstance() 方法生成。

3、JDK实现的代理中不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。CGLib可以不需要接口。

4、动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。

创建型

1.单例模式

某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。

单例模式包含如下角色:

  • Singleton:单例

二十三种设计模式(待更)

二十三种设计模式(待更)

1.1单例模式的几种实现方式

单例模式的实现有多种方式,如下所示:

1.1.1 懒汉式,线程不安全

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述: 这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

实例

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}

接下来介绍的几种实现方式都支持多线程,但是在性能上有所差异。

1.1.2 懒汉式,线程安全

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述: 这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。

优点:第一次调用才初始化,避免内存浪费。

缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

getInstance() 的性能对应用程序不是很关键(该方法使用不太频繁)。

实例

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}
1.1.3 饿汉式

是否 Lazy 初始化:

是否多线程安全:

实现难度:

描述: 这种方式比较常用,但容易产生垃圾对象。

优点:没有加锁,执行效率会提高。

缺点:类加载时就初始化,浪费内存。

它基于 classloader 机制避免了多线程的同步问题,不过,instance 在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance 方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 instance 显然没有达到 lazy loading 的效果。

实例

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}
1.1.4 双检锁/双重校验锁(DCL,即 double-checked locking)

JDK 版本: JDK1.5 起

是否 Lazy 初始化:

是否多线程安全:

实现难度: 较复杂

描述: 这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

getInstance() 的性能对应用程序很关键。

实例

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
        }  
    }  
    return singleton;  
    }  
}
1.1.5 登记式/静态内部类

是否 Lazy 初始化:

是否多线程安全:

实现难度: 一般

描述: 这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。
这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。想象一下,如果实例化 instance 很消耗资源,所以想让它延迟加载,另外一方面,又不希望在 Singleton 类加载时就实例化,因为不能确保 Singleton 类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化 instance 显然是不合适的。这个时候,这种方式相比第 3 种方式就显得很合理。

实例

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE;  
    }  
}
1.1.6 枚举

JDK 版本: JDK1.5 起

是否 Lazy 初始化:

是否多线程安全:

实现难度:

**描述:**这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。

不能通过 reflection attack 来调用私有构造方法。

实例

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

经验之谈: 一般情况下,不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。如果有其他特殊的需求,可以考虑使用第 4 种双检锁方式。

相关资料

  • 23种设计模式-思维导图

  • 23种设计模式-UML类图

  • 图说设计模式

  • 菜鸟教程

  • 《重学Java设计模式》

  • 设计模式之于我

  • 25000 字详解 23 种设计模式(多图 + 代码)文章来源地址https://www.toymoban.com/news/detail-478604.html

到了这里,关于二十三种设计模式(待更)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 二十三种设计模式第十四篇--策略模式

    策略模式:主要围绕一个类的行为或者其算法在运行时更改,也是一种行为型模式。 在软件开发中,我们经常遇到需要根据不同的情况选择不同算法或行为的情况。传统的做法是使用大量的条件语句来实现这种逻辑,但这样的实现方式往往难以维护和扩展。策略模式(Strat

    2024年02月12日
    浏览(50)
  • 二十三种设计模式第十五篇--模版方法模式

    模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,而将一些步骤延迟到子类中实现。通过使用这种模式,我们可以在不改变算法结构的情况下,重新定义算法中的某些特定步骤。 模板方法模式的核心思想是将一个算法分解为一系列步骤,并将可变的部分封装在

    2024年02月12日
    浏览(46)
  • 二十三种设计模式第十八篇--责任链模式

    责任链模式是一种行为型设计模式, 它允许你将请求沿着处理者链传递,直到有一个处理者能够处理该请求为止。责任链模式将请求发送者和请求处理者解耦,从而使得多个处理者都有机会处理同一个请求。 该模式包含以下几个关键角色: 抽象处理者(Handler):定义了一个

    2024年02月15日
    浏览(36)
  • 二十三种设计模式第二十四篇--访问者模式(完结撒花)

    在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。 通过这种方式,元素的执行算法可以随着访问者改变而改变。 这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的

    2024年02月14日
    浏览(33)
  • 二十三种设计模式第十六篇--观察者模式

    观察者模式是一种行为型设计模式,它建立了一种对象间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。这种模式可以实现对象间的松耦合通信,提高系统的可扩展性和灵活性。 观察者模式的核心是两个角色:主题(

    2024年02月12日
    浏览(36)
  • 【中级软件设计师】—(针对上午题)二十三种设计模式(三十九)

    简单工厂模式代码实现如下: 📢 意图要背 工厂方法代码实现如下: 抽象工厂模式代码如下: 生成器模式代码如下: 原型模式代码实现如下: 单例模式代码如下: 桥接模式代码实现如下: 组合模式添加和删除代码实现: 装饰器模式代码实现: 外观模式代码实现: 代理模

    2024年02月07日
    浏览(37)
  • 二十三种设计模式全面解析-抽象工厂模式:创造无限可能的工厂之道

    在软件开发中,我们经常面临着创建一系列相关或相互依赖的对象的挑战。在这个过程中,我们希望能够灵活地创建对象,又不与具体的类耦合。那么有没有一种设计模式可以帮助我们实现这一目标呢?答案是肯定的!让我带你进入抽象工厂模式的世界,一起探索创造无限可

    2024年02月07日
    浏览(37)
  • 二十三种设计模式全面解析-原型模式进阶之原型管理器:集中管理对象原型的设计模式之道

    在软件开发中,我们经常需要创建和复制对象。然而,有时候直接创建对象可能会导致性能下降或代码重复。为了解决这些问题,原型模式应运而生。而使用原型管理器(Prototype Manager)来集中管理原型对象可以进一步提高灵活性和可维护性。本文将详细介绍原型管理器的用

    2024年02月07日
    浏览(41)
  • 二十三种设计模式全面解析-装饰器模式的高级应用:打造灵活可扩展的通知系统

    在现代软件开发中,通知系统是一个广泛应用的功能,用于实时向用户发送各种类型的通知,如短信、微信、邮件以及系统通知。然而,通知系统的需求通常是多变且动态的,因此需要一种灵活可扩展的设计模式来满足不同类型的通知需求。 在前面一篇文章中,我们介绍了什

    2024年02月06日
    浏览(36)
  • 二十三种设计模式全面解析-桥接模式的高级应用:构建灵活的跨平台UI框架

    在软件开发的世界中,桥接模式(Bridge Pattern)作为一种设计模式,旨在将抽象部分与实现部分分离,从而使它们可以独立地变化。这一模式的应用不仅有助于提高代码的可维护性,还在一些复杂的场景中展现出其真正的价值。 前面一篇文章中,我们介绍了什么是桥接模式?

    2024年02月05日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包