ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

工作几年设计模式那些事~

2020-12-03 21:01:32  阅读:186  来源: 互联网

标签:getInstance class 那些 INSTANCE static 几年 new 设计模式 public


工作几年设计模式那些事~

  • List item

背景

工作几年, 随着经验的积累, 慢慢地开始往自己的代码中添加合适的设计模式, 所写代码也不再是为了单纯完成某个需求, 而是在原先基础上用更少的代码完成更多的事 , 让自己的代码更加地优雅 . 更加健壮的运行 , 下面是一些工作中用到的设计模式及读书或者博文的一些总结.

Java设计模式种类

Java语言中 , 共有23种设计模式,分别是: 单例, 策略 , 工厂和抽象工厂 , 门面Facade , 调停者Mediator , 观察者Observer , 装饰者Decorator , 责任链模式(Chain of responsibility), 组合(Composite) , 享元(Flyweight) , 代理(Proxy),迭代器(Iterator), 访问者(Visitor), 构建器(Builder) , 适配器(Adapter), 桥接(Bridge), 命令(Command/Action/Transaction), 原型/克隆(Prototype), 备忘录(Memento), TemplateMethod(模板方法/钩子函数) , State(状态), Intepreter(解释器)

单例

1.单例类的目的在于确保某一个类只有一个实例对象
2.优点是节约资源、提高效率,同时严格控制客户对它的访问。但是就导致了单例类的职责过重,违反单一职责原则

单例模式常见的写法有八种:
1.恶汉式加载

public class Instance01 {
    // 创建单例
    private static final Instance01 INSTANCE = 
    								new Instance01();
	// 私有构造,保证单例
    private Instance01() {};
	// 提供单例的方法
    public static Instance01 getInstance() {
        return INSTANCE;
    }
    public static void main(String[] args) {
        Instance01 m1 = Instance01.getInstance();
        Instance01 m2 = Instance01.getInstance();
        System.out.println(m1 == m2); // true
    }
}

2.静态代码块,类似于方法1

public class Instance02 {
    private static final Instance02 INSTANCE;
    static {
        INSTANCE = new Instance02();
    }
    private Instance02() {};
    public static Instance02 getInstance() {
        return INSTANCE;
    }
    public static void main(String[] args) {
        Instance02 m1 = Instance02.getInstance();
        Instance02 m2 = Instance02.getInstance();
        System.out.println(m1 == m2);// true
    }
}

3.懒汉式加载

public class Instance03 {
    private static Instance03 INSTANCE;
    private Instance03() {
    }
    public static Instance03 getInstance() {
        if (INSTANCE == null) {// (1)
            INSTANCE = new Instance03();
        }
        return INSTANCE;
    }
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->
                System.out.println(Instance03.getInstance().hashCode())
            ).start();
        }
    }
}

输出结果如下:
995327769
1866897138
1866897138
1866897138
770902836
2118558849
原因:多线程方法时候,(1)位置会出现多个多个线程都判断为空的情况,往下执行会new 出多个对象

4.懒汉式(synchronized)

public class Instance04 {
    private static Instance04 INSTANCE;
    private Instance04() {
    }
    public static synchronized Instance04 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Instance04();
        }
        return INSTANCE;
    }
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Instance04.getInstance().hashCode());
            }).start();
        }
    }
}

上述方法会创建唯一一个对象但是加锁模式下会影响方法执行效率

5.懒汉式(synchronized), 细粒化

public class Instance05 {
    private static Instance05 INSTANCE;
    private Instance05() {
    }
    public static Instance05 getInstance() {
        if (INSTANCE == null) {
            synchronized (Instance05.class) {
                INSTANCE = new Instance05();
            }
        }
        return INSTANCE;
    }
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Instance05.getInstance().hashCode());
            }).start();
        }
    }
}

上述方法虽然与方法4相比synchronized细粒化,但是仍然会出现方法3的多线程并发问题

6.懒汉式(synchronized), 细粒化,双重检查

public class Instance06 {
    private static volatile Instance06 INSTANCE; 
    private Instance06() {
    }
    public static Instance06 getInstance() {
        if (INSTANCE == null) {
            synchronized (Instance06.class) {
                if(INSTANCE == null) {
                    INSTANCE = new Instance06();
                }
            }
        }
        return INSTANCE;
    }
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Instance06.getInstance().hashCode());
            }).start();
        }
    }
}

在方法5的基础上多加了个if判断,不会出现多个实例的问题,但是synchronized同样会影响效率

7.静态内部类

public class Instance07 {
    private Instance07() {
    }
    private static class Mgr07Holder {
        private final static Instance07 INSTANCE = new Instance07();
    }
    public static Instance07 getInstance() {
        return Mgr07Holder.INSTANCE;
    }
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Instance07.getInstance().hashCode());
            }).start();
        }
    }
}

静态内部类的加载只有在getInstance方法被调用的时候才会被类加载,实现了懒汉式,同时应用了方法1,在实现单例的基础上同时又不失效率

8.枚举单例

public enum Instance08 {
    INSTANCE;
    public void m() {}
    public static void main(String[] args) {
        for(int i=0; i<100; i++) {
            new Thread(()->{
                System.out.println(Instance08.INSTANCE.hashCode());
            }).start();
        }
    }
}

项目开发之初,都会定义一个错误码表,在项目中,会把这些错误码放到一个统一的枚举类中,如果要是多线程情况下会产生枚举值乱的情况,那项目岂不是乱套了~~~~~~~~~

策略模式

简单的来讲,在程序中策略模式就是实现某一个功能的具体方式, 策略模式巧妙的运用了开闭原则,例如:针对苹果的比较 , 可以有大小,品相,甜度等等 ,没一个比较方式就是一个策略,我们可以使用Comparator接口为每一个比较方式提供出来比较类
策略模式图如下:
在这里插入图片描述

工厂和抽象工厂

工厂,顾名思义,在工厂中可以生产各种物件,食物 , 相等在java语言中工厂则是提供某个类,该类中提供了一些对象的创建方法,调用该方法可以创建某些对象
1.简单工厂
简单工厂实现过程为非抽象类,扩展性不好

public class SimpleFactory {
    public SmallCar createCar() {
        return new SmallCar();
    }
}
class SmallCar{   
}

以上代码块如果我想创建一个MiddleCar或者BigCar之能对原先的代码进行修改,违反了开闭原则
2.抽象工厂
是对简单工厂的升级:

/**
 * 抽象工厂类
 * 其中Food等都是抽象方法,具体的实现逻辑用户可以自己做
 */
public abstract class AbastractFactory {
    abstract Food createFood();
    abstract Vehicle createVehicle();
    abstract Weapon createWeapon();
}

类图如下:
在这里插入图片描述

门面Facade 和 调停者Mediator

门面:对外而言
类似于行政大厅的办理窗口,对于政府内部材料办公流程全然不知,我们只需在办理窗口提交相关材料即可
调停者:对内而言
可以理解为系统内部协调处理的中间人,典型案例:消息中间件

观察者Observer

习惯性的把观察者理解为偷窥狂,特务 ; 因为观察者是根据其他代码的执行情况做出的一系列反应(观察者根据被观察者的事件做出的反应),比如sptingboot 整合kafka的listener注解就是根据kafka中的消息进行的操作,常见的观察者距离还有hook函数,callback回调函数,handle,listener,spring aop切面,js点击事件,鼠标浮动事件…
类图如下:
在这里插入图片描述

装饰者Decorator

装修工,在原来的基础上进行一系列操作,亦或是使代码执行效率更快等等
举个例子:
“小姑娘化妆”
Person -> 上衣,下衣,鞋子,裙子化妆方式多种多样,怎么样实现
让上衣,下衣,鞋子,继承统一的最高类,Person在调用化妆方法的时候传入这个最高类的对象,就可以任意搭配

具体类图如下:
在这里插入图片描述

责任链模式(Chain of responsibility)

举个例子:某个人来到行政大厅办理身份证,就需要经过该信息录入->审核->制卡->快递一列流程,如果把其中的每个点看成一个责任点,那么整个流程就是一个责任链
java中的doFilter就是典型的责任链处理流程

组合(Composite)

组合模式用来将多个对象形成树形结构以表示“整体-部分”的结构层次,树形结构专用

享元(Flyweight)

顾名思义:元素共享,java的线程池,数据库连接池,以及jvm中的运行时常量池中的元素用的就是享元思想
在这里插入图片描述

代理(Proxy)

代理模式在与分离代理行为和被代理对象,替被代理对象干某件事
java中常见的代理有JDK动态代理和Cglib代理
1.JDK动态代理

Movable a = (Movable)Proxy.newProxyInstance(
				// 接口实现类的类加载器 
				Car.class.getClassLoader(),
				// 实现的接口数组
                new Class[]{Movable.class},
                // 具体处理
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // do something
                        return null;
                    }
                }
        );
        a.move(); 

使用System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,“true”);把产生的代理类save下来

public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    public final void move() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

执行原理:在最初代码块中执行Proxy.newProxyInstance会产生代理类的对象,产生对象势必会调用代理类的构造方法,

 protected Proxy(InvocationHandler h) {
       Objects.requireNonNull(h);
       this.h = h;
   }

会将第一段代码中InvocationHandler 的实现在Proxy中初始化, 当调用
a.move时,因为产生的是 $Proxy0的对象,所以会调用 $Proxy0的move方法
执行以下代码:

super.h.invoke(this, m3, (Object[])null);

因为之前h已经被Proxy初始化所以这个时候的invoke方法执行的是自己实现的InvocationHandler invoke方法中的逻辑
2.Cglib代理

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Car.class);
        enhancer.setCallback(new TimeMethodInterceptor());
        Car car = (Car)enhancer.create();
        car.move();
    }
}
class TimeMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
      // do something
    }
}

缺点:被代理的类Car不能是final 否则执行会报错

迭代器(Iterator)

一般用于容器的遍历,提供了一种游走于各个元素之间的遍历方式,
在这里插入图片描述

访问者(Visitor)

访问者模式适用于那些结构不变的对象 , 目的是封装一些针对某些数据结构元素之上的操作,一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变
在这里插入图片描述

构建器(Builder)

可以将一个属性特别多的对象的构建过程简单化,假如一个对象拥有100个属性但是其中只有3个真实需要的属性,但是每次在创建这个对象的时候都需要传入剩余97个属性的值,很明显这种方法是不合理的,这时候我们就可以用到构建器,每次创建对象的时候只传必要的属性,灵活可变,如下:

public class Person {
    int id;
    String name;
    int age;
    double weight;
    int score;
    private Person() {}
    public static class PersonBuilder {
        Person p = new Person();
        public PersonBuilder basicInfo(int id, String name, int age) {
            p.id = id;
            p.name = name;
            p.age = age;
            return this;
        }
        public PersonBuilder weight(double weight) {
            p.weight = weight;
            return this;
        }
        public PersonBuilder score(int score) {
            p.score = score;
            return this;
        }
        public Person build() {
            return p;
        }
    }
}

在这里插入图片描述

适配器(Adapter)

我理解的适配器是在某些不兼容场景下, 需要有一个中间处理的环节 ,这个环节具体化就是一个适配器,例如:
1.Java流操作InputStreamReader 和 OutputStreamWriter

public static void main(String[] args) throws Exception {
        File f = new File("c:/work/test.data");
        FileOutputStream fos = new FileOutputStream(f);
        OutputStreamWriter osw = new OutputStreamWriter(fos);
        BufferedWriter bw = new BufferedWriter(osw);
        bw.write("http://www.mashibing.com");
        bw.flush();
        bw.close();
    }

2.java连接mysql的jar包是mysql-connector-java, 连接sqlserver的包是odbc 但是odbc是不能访问的, 如果想要访问就需要在jdbc和jdbc之间加一个适配器

桥接(Bridge)

桥接模式的精髓在于分离抽象与具体实现
举个简单的例子
A向B赠送礼物,礼物Gift有大的BigGift, 中的MiddleGift , 小的SmallGift
其中大的礼物有大象,坦克,大炮, 中的礼物有电脑,手机,鼠标 小的礼物有蜂鸟,鱼… 桥梁模式可以在抽象主线(Gift) 和 实现主线(GiftImpl)上并行发展 在抽象主线的实现类的构造中传入顶级实现主线父类(GiftImpl)便是巧妙地运用了桥接模式,类图如下:
在这里插入图片描述

命令(Command/Action/Transaction)

命令模式一般有do和undo, do是指进行某一操作,undo 是指相应do命令命令的撤销
在这里插入图片描述

原型/克隆(Prototype)

BeanUtils.copyProperties
对象的克隆

备忘录(Memento)

记录快照
存盘
序列化

TemplateMethod(模板方法/钩子函数)

技术源码中最常用的设计模式之一
方法重写
Service层的方法不同实现
在这里插入图片描述

State(状态)

根据状态的不同进行一系列的不同操作
在这里插入图片描述

Intepreter(解释器)

用来做动态脚本解析

标签:getInstance,class,那些,INSTANCE,static,几年,new,设计模式,public
来源: https://blog.csdn.net/weixin_43769053/article/details/110502249

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有