设计模式

说一说设计模式的六大原则

单一职责原则

一个类,应当只有一个引起它变化的原因;即一个类应该只有一个原则。

就一个类而言,应该只专注于做一件事和仅有一个引起变化的原因,着就是所谓的单一职责原则。该原则提出了对对象职责的一种理想状态,对象不应该承担太多职责,正如人不应该一心分为二用。唯有专注,才能保证对象的高内聚;唯有单一才能保证对象的细粒度。对象的高内聚和细粒度有利于对象的重用。

里氏替换原则

所有引用基类的地方必须能透明地使用其子类对象。清晰明确的说明只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误和异常,使用者可以根本就不需要知道是父类还是子类;但是反过来不可以,有子类的地方,替换为父类就未必适应。

依赖倒置原则

依赖倒置原则包括三种含义:
• 高层模块不应该依赖低层模块,两者都依赖其抽象;
• 抽象不依赖细节;
• 细节应该依赖于抽象。
传统的过程性系统的设计办法倾向于高层次的模块依赖于低层次的模块;抽象层次依赖于具体层次。“倒置”原则将这个错误的依赖关系倒置了过来,如下图所示,由此命名为“依赖倒置原则”。

 

在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是具体的实现类,实现类实现了接口或者继承了抽象类,其特点是可以直接被实例化的。依赖倒置原则在Java语言中的表现是:

(1)模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生;

(2)接口或抽象类不依赖于实现类;

(3)实现类依赖于接口或抽象类。

依赖倒置原则更加精确的定义就是”面向接口编程“————OOD(Object-Oriented Design)的精髓之一。依赖倒置原则可以减少类间的耦合性,提高系统的稳定性,降低并行开发引起的风险,提高代码的可读性和可维护性。依赖倒置原则是JavaBean,EJB和COM等组件设计模型背后的基本原则。

接口隔离原则

接口隔离原则有如下两种定义:

(1)客户端不应该依赖它不需要的接口;

(2)类间的依赖关系应该建立在最小的接口上。

接口隔离原则的具体含义如下:

(1)一个类对另外一个类的依赖性应当是建立在最小的接口上。

(2)一个接口代表一个角色,不应当将不同的角色都交给一个接口。

(3)不应该强迫客户依赖于他们不用的方法。

迪米特法则

迪米特法则又称为最少知识原则,意思是一个对象应当对其他对象尽可能少的了解。迪米特法则不同于其他的OO设计原则,它具有很多的表述方式:

(1)只与你直接的朋友们通信;

(2)不要跟”陌生人说话“;

(3)每个软件单位对其他的单位都只有最少的了解,这些了解仅局限于那些与本单位密切相关的软件单位。

按照迪米特法则,如果两个类不必彼此直接通信,那么两个类就不应当发生直接的相互作用;如果一个类需要调用另一个类的某个方法,可以通过第三者转发这个调用。

开闭原则

开闭原则的定义是:一个软件实体应当对扩展开放,对修改关闭。在设计一个模块的时候,应当使这个模块在可以不被修改的前提下被扩展,即应当可以在不必修改源代码的情况下改变这个模块的行为。

在面向对象的编程中,开闭原则是最基础的原则,起到总的指导作用,其他原则(单一职责、里氏替换、依赖倒置、接口隔
离、迪米特法则)都是开闭原则的具体形态,即其他原则都是开闭原则的手段和工具。开闭原则的重要性可以通过以下几个
方面来体现。

设计模式分类

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

创建型模式

工厂模式

定义:定义了一个创建对象的类,由这个类来封装实例化对象的行为。

 工厂模式的用意是定义一个创建对象的工厂接口,将实际创建性工作推迟到子类中。工厂模式可以分为简单工厂、工厂方法和抽象工厂模式。注意,我们常说的23种经典设计模式,包含了工厂方法模式和抽象工厂模式,而并未包含简单工厂模式。

简单工厂

简单工厂模式其实并不是一种设计模式,更多的时候是一种编程习惯。简单工厂的实现思路是,定义一个工厂类,根据传入参数的不同返回不同的实例,被创建的实例具有共同的父类或接口。

 

 为工厂类传入不同的type可以new不同的形状,返回结果为shape类型,这个就是简单工厂核心的地方了。

工厂方法

工厂方法模式是简单工厂的进一步深化,在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。工厂方法的实现思路是,定义一个用于创建对象的接口,让子类决定将哪一个实例化。

和简单工厂对比一下,最根本的区别在于,简单工厂只有一个统一的工厂类,而工厂方法是针对每个要创建的对象都会提供一个工厂类,这些工厂类都实现了一个工厂基类。

抽象工厂

这个模式最不好理解,而且在实际应用中局限性也蛮大的,因为这个模式并不符合开闭原则。实际开发还需要做好权衡。抽象工厂模式是一个工厂方法的进一步深化,这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象,这是和工厂方法最大的不同点。

抽象工厂的实现思路是,提供一个创建一些列相关或相互依赖对象的接口,而无需指定他们具体的类。抽象工厂和工厂方法一样可以划分为4大部分。

 

 

 简单工厂模式和抽象工厂模式的区别

简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。简单工厂的实现思路是,定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。
(一个工厂创建几种对象)
工厂方法模式是简单工厂的进一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。工厂方法的实现思路是,定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
(多个工厂,不同的工厂创建不同的对象)
抽象工厂模式是工厂方法的进一步深化,在这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象。这是和工厂方法最大的不同点。抽象工厂的实现思路是,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。

 (多个工厂,一个工厂可以创建一组对象)

简单工厂模式就是建立一个实例化对象的类,在该类中对多个对象实例化。工厂方法模式是定义了一个创建对象的抽象方法,由子类决定要实例化的类。这样做的好处是再有新的类型的对象需要实例化只要增加子类即可。抽象工厂模式定义了一个接口用于创建对象族,而无需明确指定具体类。抽象工厂也是把对象的实例化交给了子类,即支持拓展。同时提供给客户端接口,避免了用户直接操作子类工厂。

单例模式

定义:确保一个类最多只有一个实例,并提供一个全局访问点

单例模式可以分为两种:预加载和懒加载

 饿汉式单例模式

public class Singleton {
    private static Singleton instance = new Singleton();
    //私有构造方法,保证外界无法直接实例化
    private Singleton(){}
    //通过公有静态方法获取实例对象
    public static Singleton getInstance(){
        return instance;
    }
}

懒汉式单例模式

public class Singleton {
    private static Singleton instance = null;
    //私有构造方法,保证外界无法直接实例化
    private Singleton(){}
    //通过公有静态方法获取实例对象
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

线程安全的单例模式

public class Singleton {
    private static Singleton instance = null;
    //私有构造方法,保证外界无法直接实例化
    private Singleton(){}
    //通过公有静态方法获取实例对象
    synchronized public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
上述代码对静态方法 getInstance()进行同步,以确保多线程环境下只创建一个实例。如果getInstance()方法未被同步,并且线程A和线程B同时调用此方法,则执行if (instance == null)语句时都为真,那么线程A和线程B都会创建一个对象,在内存中就会出现两个对象,这样就违反了单例模式。而使用synchronized关键字进行同步后,则不会出现此种情况。

 单例模式优点:

(1)在单例模式中,活动的单例只有一个实例,对单例的所有实例化得到的都是相同的一个实例。这样就,防止其他对象对自己的实例化,确保所有对象都访问一个实例。

(2)提供了对唯一实例的受控访问。

(3)由于系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。

(4)避免对共享资源的多重占用。

缺点:

(1)不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。

(2)由于单例模式没有抽象层,因此单例类的扩展有很大的困难。

(3)单例类的职责过重,在一定程度上违背了”单一职责原则‘。

(4)使用时不能用反射模式创建单例,否则会实例化一个新的对象。

生成器模式

定义:封装一个复杂对象构造过程,并允许按步骤构造。

定义解释: 我们可以将生成器模式理解为,假设我们有一个对象需要建立,这个对象是由多个组件(Component)组合而成,每个组件的建立都比较复杂,但运用组件来建立所需的对象非常简单,所以我们就可以将构建复杂组件的步骤与运用组件构建对象分离,使用builder模式可以建立。

优点:

  • 将一个对象分解为各个组件

  • 将对象组件的构造封装起来

  • 可以控制整个对象的生成过程

缺点:

  • 对不同类型的对象需要实现不同的具体构造器的类,这可能回答大大增加类的数量

生成器模式与工厂模式的不同

生成器模式构建对象的时候,对象通常构建的过程中需要多个步骤,就像我们例子中的先有主机,再有显示屏,再有鼠标等等,生成器模式的作用就是将这些复杂的构建过程封装起来。工厂模式构建对象的时候通常就只有一个步骤,调用一个工厂方法就可以生成一个对象。

原型模式

定义:通过复制现有实例来创建新的实例,无需知道相应类的信息。

简单地理解,其实就是当需要创建一个指定的对象时,我们刚好有一个这样的对象,但是又不能直接使用,我会clone一个一模一样的新对象来使用;基本上这就是原型模式。关键字:Clone。

原型模式的本质就是clone,可以解决构建复杂对象的资源消耗问题,能再某些场景中提升构建对象的效率;还有一个重要的用途就是保护性拷贝,可以通过返回一个拷贝对象的形式,实现只读的限制。

结构型模式

适配器模式

定义: 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,目的是消除由于接口不匹配所造成的类的兼容性问题。

主要分为三类:类的适配器模式、对象的适配器模式、接口的适配器模式。

类适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。

对象适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个Wrapper类,持有原类的一个实例,在Wrapper类的方法中,调用实例的方法就行。

接口适配器模式:当不希望实现一个接口中所有的方法时,可以创建一个抽象类Wrapper,实现所有方法,我们写别的类的时候,继承抽象类即可。

装饰者模式

定义:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性。

中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。

装饰者和被装饰者之间必须是一样的类型,也就是要有共同的超类。在这里应用继承并不是实现方法的复制,而是实现类型的匹配。因为装饰者和被装饰者是同一个类型,因此装饰者可以取代被装饰者,这样就使被装饰者拥有了装饰者独有的行为。根据装饰者模式的理念,我们可以在任何时候,实现新的装饰者增加新的行为。如果是用继承,每当需要增加新的行为时,就要修改原程序了。

代理模式

定义:代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。

代理模式分为三类:1. 静态代理 2. 动态代理 3. CGLIB代理

外观模式

定义: 隐藏了系统的复杂性,并向客户端提供了一个可以访问系统的接口。

  - 松散耦合

  使得客户端和子系统之间解耦,让子系统内部的模块功能更容易扩展和维护;

  - 简单易用

  客户端根本不需要知道子系统内部的实现,或者根本不需要知道子系统内部的构成,它只需要跟Facade类交互即可。

  - 更好的划分访问层次

 有些方法是对系统外的,有些方法是系统内部相互交互的使用的。子系统把那些暴露给外部的功能集中到门面中,这样就可以实现客户端的使用,很好的隐藏了子系统内部的细节。

桥接模式

定义: 将抽象部分与它的实现部分分离,使它们都可以独立地变化。

优点:

(1)在很多情况下,桥接模式可以取代多层继承方案,多层继承方案违背了“单一职责原则”,复用性较差,且类的个数非常多,桥接模式是比多层继承方案更好的解决方法,它极大减少了子类的个数。

(2)桥接模式提高了系统的可扩展性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统,符合“开闭原则”。

缺点:

桥接模式的使用会增加系统的理解与设计难度,由于关联关系建立在抽象层,要求开发者一开始就针对抽象层进行设计与编程。

 Spring框装架中用到了哪些设计模式?

 

①简单工厂
Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象,但是否是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。
②工厂方法
实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是,spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean的getOjbect()方法的返回值。
③单例模式
Spring依赖注入Bean实例默认是单例的。Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。getBean的doGetBean方法调用getSingleton进行bean的创建。
④适配器模式
SpringMVC中的适配器HandlerAdatper,它会根据Handler规则执行不同的Handler。即DispatcherServlet根据HandlerMapping返回的handler,向HandlerAdatper发起请求处理Handler。HandlerAdapter根据规则找到对应的Handler并让其执行,执行完毕后Handler会向HandlerAdapter返回一个ModelAndView,最后由HandlerAdapter向DispatchServelet返回一个ModelAndView。
⑤代理模式
AOP底层就是动态代理模式的实现。即:切面在应用运行的时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象创建动态的创建一个代理对象。SpringAOP就是以这种方式织入切面的。
⑥策略模式
Spring框架的资源访问Resource接口。该接口提供了更强的资源访问能力,Spring 框架本身大量使用了 Resource 接口来访问底层资源。Resource 接口是具体资源访问策略的抽象,也是所有资源访问类所实现的接口。
 

 

 

 

 

 

 

 

 

 

 

posted @ 2023-02-17 10:39  白非立  阅读(71)  评论(0)    收藏  举报