30岁程序员的未来心路

Java基础-设计模式详解

2021-08-23  本文已影响0人  夜暗殇

一、设计模式的分类

总体来说设计模式分为三大类:

<1> 创建型模式:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式

<2> 结构型模式:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式

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


二、设计模式的六大原则

<1> 开闭原则--Open Close Principle

开闭原则:对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。意思就是:为了使程序的扩展性好,易于维护和升级。

<2> 里氏代换原则--Liskov Substitution Principle

里氏代换原则(Liskov Substitution Principle LSP)面向对象涉及的基本原则之一。

任何基类可以出现的地方,子类一定可以出现。LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

<3> 依赖倒转原则--Dependence Inversion Principle

依赖倒转原则是开闭原则的基础,具体解释为:针对接口编程,依赖于抽象而不依赖于具体。

<4> 接口隔离原则--Interface Segregation Principle

此原则的意思是:使用多个隔离的接口比使用单个接口要好。能够降低类之间的耦合度。从这里我们就能看出设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便:降低依赖,降低耦合。

<5> 迪米特法则(最少知道原则)--Demeter Principle

一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

<6> 合成复用原则--Composite Reuse Principle

尽量是用合成/聚合的方式,而不是使用继承。


Java中的23种设计模式

1、工厂模式--Factory Method

工厂模式分为三种:

<1> 普通工厂模式

建立一个工厂类,对实现了同一接口的一些类进行实例的创建,如图:

例子:发送邮件和短信

首先,创建一个共同的接口

public interface Sender {

public void Send();

}

其次,创建发送短信和邮件的实现类

public class MailSender implements Sender {

    @Override public void Send() {

        System.out.println("this is mailsender!");

    }

}

public class SmsSender implements Sender {

    @Override public void Send() {

        System.out.println("this is sms sender!");

    }

}

最后创建工厂类:

public class SendFactory {

    public Sender produce(String type) {

        if ("mail".equals(type)) {

            return new MailSender();

        } else if ("sms".equals(type)) {

            return new SmsSender();

        } else {

            System.out.println("请输入正确的类型!");

            return null;

            }

        }

测试类:

public class FactoryTest {

    public static void main(String[] args) {

        SendFactory factory = new SendFactory();

        Sender sender = factory.produce("sms");

        sender.Send();

    }

输出: this is sms sender!


<2> 多个工厂方法模式

该模式是对普通工厂方法模式的改进,在扑通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而多个工厂方法模式是提供多个工厂方法,分别创建对象。

将普通工厂模式的SendFactory 进行修改

public class SendFactory {

    public Sender produceMail(){

        return new MailSender();

    }

    public Sender produceSms(){

        return new SmsSender();

    }

测试类:

public class FactoryTest{

    public static void main(String[] args){

        SendFactory factory = new SendFactory();

        Sender sender =factory.produceMail();

        sender.Send();

    }

}

输出: this is mailsender!


<3> 静态工厂方法模式

将多个工厂方法模式中的方法置为静态的,不需要创建实例,直接调用即可。

public class SendFactory { 

  public static Sender produceMail(){

        return new MailSender();

    }

    public static Sender produceSms(){

        return new SmsSender();

    }

测试类:

public class FactoryTest{

    public static void main(String[] args){

        Sender sender = SenderFactory.produceMail();

        sender.Send();

    }

}

输出:this is mailsender!

总体来说:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。在以上的三种模式中,第一种如果传入的字符串有误,不能正确创建对象,第二种解决了此问题,但需进行实例化。第三种相比第二种就不需要实例化工厂类。因此大多情况下会选用第三种---静态工厂方法模式。


2、抽象工厂模式--Abstract Factory

举例:

    接口:

public interface Sender {

    public void Send();

}

    两个实现类:

public class MailSender implements Sender{

    @Override

    public void Send(){

        System.out.println("this is mailsender!");

    }

}

public class SmsSneder implements Sender{

    @Override

    public void Send(){

        System.out.println("this is sms sender!")

    }

}

    通用接口:

public interface Provider{

    public Sender produce();

}

    两个工厂类:

public class SenderMailFactory implements Provider{

    @Override

    public Sender produce(){

        return new MailSender();

    }

}

public class SendSmsFactory implements Provider{

    @Override

    public Sender produce(){

        return new SmsSender();

    }

}

    测试类:

public class Test{

    public static void main(String[] args){

        Provider provider = new SendMailFactory();

        Sender sender = provider.produce();

        sender.Send();

    }

}

此模式的好处是:如果现在想增加一个功能,只需再写一个实现类,实现Sender接口,同时做一个工厂类,实现Provider接口就可以了,无需改动现有代码。


3、单例模式--Singleton

单例模式是设计模式中最常见也是最简单的一种设计模式,保证了在程序中只有一个实例存在并且能全局访问到。

适用场景:

--应用中某个实例对象需要频繁的被访问

--应用中每次启动只会存在一个势力。如账号系统、数据库系统。

常用的使用方式

<1> 懒汉式:

    优点:延迟加载(需要的时候采取加载)

    缺点:线程不安全,在多线程中很容易出现不同步的情况,如在数据库对象进行的频繁读写操作时。

    具体实现:

public class Singleton{

    /*持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载*/

    private static Singleton instance = null;

    /*私有构造方法,防止被实例化*/

    private Singleton(){};

    /*1:懒汉式,静态方法,创建势力*/

    public static Singleton getInstance(){

        if(instance == null){

            instance = new Singleton();

         }

          return instance;

    }

}

<2> 加同步锁

    优点:解决了线程不安全的问题

    缺点:效率低,每次调用实例都要判断同步锁

pubic static synchronized Singleton getInstance(){

    if(instance == null){

        instance = new Singleton();

    }

    return instance;

}

    另一种写法:

/*加上synchronized,但是每次滴啊用实力都会加载*/

public static Singleton getInstance(){

    synchronized(Singleton.class){

     if(nstance == null){

        instance = new Singleton();

      }

    }

    return instance;

}

<3> 双重检验锁

要优化(2)中因为每次调用实例都要判断同步锁的问题,很多人都使用下面的一种双重判断校验的办法。

优点:在并发量不多,安全性不高的情况下或许能很完美运行单例模式

缺点:不同平台编译过程中可能会存在严重安全隐患。

/*3.双重锁定:只在第一次初始化的时候加上同步锁*/

public static Singleton getInstance() {

    if (instance == null) {

        synchronized (Singleton.class) {

            if (instance == null) {

                instance = new Singleton();

            }

        }

    }

    return instance;

}

这种方法貌似很完美的解决了上述效率问题,在并发量不多,安全性不太高的情况下或许能完美运行。但是,这种方法也有问题,出现在这句:

instance = newSingleton();

在JVM编译的过程中会出现指令重排的优化过程,这就会导致当instance实际上还没初始化,就可能被分配了内存空间,也就是会出现instance!=null,但是又没初始化的情况,这样就会导致返回的instance不完整。

<4> 内部类的实现

优点:延迟加载,线程安全(Java中class加载时互斥的),减少了内存消耗。内部类是一种好的实现方式。

public class SingletonInner{

    private static class SingletonHolder{

        private static SingletonInner instance = new SingletonInner();

    }

    /*私有构造函数*/

    private SingletonInner(){}

    public static SingletonInner getInstance(){

        return SingletonHolder.instance;

    }

    protected void method(){

        System.out.println("SingletonInner");

    }

}

<5> 枚举

public enum SingletonEnum {

    /** *

        1.从Java1.5开始支持;

      * 2.无偿提供序列化机制;

      * 3.绝对防止多次实例化,即使在面对复杂的序列化或者反射攻击的时候;

    */

    instance;

    private String others;

    SingletonEnum() {

    }

    public void method() {

        System.out.println("SingletonEnum");

    }

    public String getOthers() {

        return others;

    }

    public void setOthers(String others) {

        this.others = others;

    }

文章整理于知乎java后端开发(链接地址:https://zhuanlan.zhihu.com/p/93770973)

上一篇下一篇

猜你喜欢

热点阅读