java--设计模式 --单例and工厂


设计模式

概念:模式是一套被反复使用、多数人知晓的、经过分类编写的、代码设计经验的总结;它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。


提示:以下是本篇文章正文内容,下面案例可供参考

一、设计模式

模式好的代码经验的总结

作用

  1. 提高程序员能力
  2. 提高程序标准化工程化 效率提高 缩短开发周期
  3. 让代码可重用性高,可读,可靠,灵活性 可维护性强

类别

三大类 23种

  1. 创建型模式 :::工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
  2. 结构型模式:::适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
  3. 行为型模式:::策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

设计模式遵循的原则

  1. 开闭原则(Open Close Principle)

    对扩展开放,对修改关闭.
    当对正在使用的程序进行升级和维护的时候需要对软件原有代码进行修改时 可能会对以前的代码中引入错误也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
    解决方案:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

  2. 里氏代换原则(Liskov Substitution Principle)

    拿子类替换父类的原则 可以扩展父类的功能但不改变父类的功能
    如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1代换o2时,程序P的行为没有变化,那么类型S是类型T的子类型。

  3. 依赖倒装原则(Dependence inversion Principle)

这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体。高层模块不应该依赖低层模块,两者都应该依赖其抽象,抽象不应该依赖细节,细节应该依赖抽象

  1. 接口隔离原则(interface Segregation Principle)

    使用多个隔离的接口来降低螯合度
    问题:类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
    解决方案:将臃肿的接口I拆分为独立的几个接口,类A和类C分别与他们需要的接口建立依赖关系。也就是采用接口隔离原则。

  2. 迪米特法则()(Demeter Principle)
    不要和陌生人说话
    一个实体应当尽量少的与其他实体之间互相作用,让系统的功能模块相对独立
    问题:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
    解决方法:尽量降低类与类之间的耦合。

  3. 合成复用原则(compositeReusePrinciple)

原则是尽量使用合成/聚合的方式,而不是使用继承。继承实际上破坏了类的封装性,超类的方法可能会被子类修改。
问题:B类如果继承了A类,A类可继承方法m的实现细节暴露给B类,如果A类发生方法m改变,那么B的实现也不得不发生改变

聚合:B的对象销毁了,对A的对象没有任何影响

合成:B的对象销毁了, A的对象也不存在
7. 单一职责原则(Single responsibility principle)

一个类只负责一个功能领域的响应职责。

如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。另外,多个职责耦合在一起,会影响复用性。

问题:比如一个类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变而需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。

解决方法:遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改类T1时,不会使职责P2发生故障风险;同理,当修改T2时,也不会使职责P1发生故障风险。

二、常用的设计模式

1.工厂模式(Factory Method)

总概念:
工厂模式属于创建型设计模式,它提供了一种创建对象的最佳方式。隐藏复杂的逻辑处理过程, 只关心执行结果。直接用new可以完成的不需要用工厂模式.需要生成复杂对象的地方使用

1.1、简单工厂模式静态工厂模式

1.1.1.概念

在简单工厂模式中,可以根据参数的不同返回不同的类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例

1.1.2.优点

功能强大,创建对象和使用对象分离,程序员只关心对象使用,不用关心对象如何创建

1.1.3.缺点

耦合度高(所有产品都在工厂中成绩,一但异常工厂内的所有产品都会受到影响),扩展性也不强(每次添加工厂类都要变化),违背开闭原则

1.1.4.实例

代码如下(示例):
接口

 package org.example.dp.factory2.simple;
//软件技术
public interface SoftwareTechnology
{
    
    
    void studyST();
}

实现类

/**
 * @author zhangyifan
 * @version 8.0
 * @description:java技术
 * @date 2021/12/2 14:30
 */
public class javaDevTechnology implements SoftwareTechnology{
    
    
    @Override
    public void studyST() {
    
    
        System.out.println("学习java技术");
    }
}
/**
 * @author zhangyifan
 * @version 8.0
 * @description: Python 软件技术
 * @date 2021/12/2 14:31
 */
public class PythonDevTechnology implements SoftwareTechnology {
    
    
    @Override
    public void studyST() {
    
    
        System.out.println("学习python技术");
    }
}

工厂

/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 14:34
 */
public class AAAFactory {
    
    
    //方法
    public SoftwareTechnology teschST(int type){
    
    
        if (type==1){
    
    
            return new PythonDevTechnology();
        }else if (type==2){
    
    
            return new javaDevTechnology();
        }else {
    
    
            return null;
        }
    }
}

测试类


/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 14:34
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        AAAFactory aaaFactory=new AAAFactory();
        SoftwareTechnology softwareTechnology = aaaFactory.teschST(1);
        if (softwareTechnology!=null){
    
    
            softwareTechnology.studyST();
        }else {
    
    
            System.out.println("没有这个技术");
        }
    }
    /*学习python技术*/
}

1.2、工厂(方法)模式

1.2.1、概念

工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到期子类。

工厂本身不在创建产品、而是规定来工厂的规范,就是工厂接口,而将产品创建都交给子工厂创建

1.2.2、优点

遵循了开闭原则(不需要修改工厂类,就可以增加产品)
解耦,职责单一(每个工厂就只用负责对应的产品)

1.2.3、缺点

增加系统复杂度(每一个产品都需要一个新增加一个工厂)

实例

代码如下(示例):
工厂规范

public interface AAAStanderFactory {
    
    
    /* 制定规范 工厂接口 利用之前的接口标准*/
    SoftwareTechnology teachST();
}

产品实现子工厂类

/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 14:51
 */
public class BejingAAAFactory implements AAAStanderFactory{
    
    
    /*   遵循了开闭原则(不需要修改工厂类,就可以增加产品)*/
    /*    解耦,职责单一(每个工厂只负责创建对应的产品)*/
    @Override/* 增加系统复杂度(每新加一个产品需要新加一个工厂)*/
    public SoftwareTechnology teachST() {
    
    
        return new javaDevTechnology();
    } 
}
/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 14:55
 */
public class WuHanAAAFactory implements AAAStanderFactory{
    
    
    @Override/**/
    public SoftwareTechnology teachST() {
    
    
        return new PythonDevTechnology();
    }
}


测试类


/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 14:56
 */
public class TEst {
    
    
    public static void main(String[] args) {
    
    
        AAAStanderFactory aaaStanderFactory=new BejingAAAFactory();
        aaaStanderFactory.teachST().studyST();
        AAAStanderFactory aaaStanderFactory1=new WuHanAAAFactory();
        aaaStanderFactory1.teachST().studyST();
    }
}

结果
学习java技术
学习python技术

1.3、抽象工厂

1.3.1概念

抽象工厂是工厂方法的升级版,为相关或者相互依赖的对象提供一个接口,而且无需指定具体实现类

抽象类的工厂模式是针对多个产品系列的,工厂方法是一个产品系列一个工厂类 ,二抽象工厂是多个产品系列·一个工厂类

1.3.2优点

当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只能使用同一个2产品族中的对象。

1.3.3缺点

难以支持新种类的产品。因为抽象工厂接口确定了可以被创建的产品集合。所有难以扩展抽象工厂以产生新种类的产品。

代码如下(示例):

定义接口

/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 15:11
 */
public interface DiggerTechnology {
    
    
    void stuDT();
}

/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 15:10
 */
public interface AAAStractFactory {
    
    
    /* 缺点:难以支持新种类的产品。因为抽象工厂接口确定了可以被创建的产品集合,所以难以扩展抽象工厂以生产新种类的产品。*/
    /* 软件技术*/
    SoftwareTechnology teachIT();
    /* 挖掘机技术*/
    DiggerTechnology teachDT();
}

实现工厂

/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 15:17
 *//* 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象*/
public class ShenZhenFactory implements AAAStractFactory{
    
    
    @Override
    public SoftwareTechnology teachIT() {
    
    
        return new PythonDevTechnology();/* python*/
    }

    @Override
    public DiggerTechnology teachDT() {
    
    
        return new GrabTechnology();/* 开挖掘机*/
    }
}


测试

/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 15:19
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        AAAStractFactory aaaStractFactory=new ShenZhenFactory();
     /*   aaaStractFactory.teachDT().stuDT(); 这样会分不清
        aaaStractFactory.teachIT().studyST();*/
        System.out.println("========");
        DiggerTechnology diggerTechnology=aaaStractFactory.teachDT();
        diggerTechnology.stuDT();
        SoftwareTechnology softwareTechnology=aaaStractFactory.teachIT();
        softwareTechnology.studyST();
    }
}

2.单例(态)模式(Singleto)

概念

一种常用的软件设计模式,所谓单列,就是一个类在项目运行中只存在一个对象,即使用到这个类的地方很多,也只存在一个对象。

优点

  1. 节省内存
  2. 有些情况下不用单例模式可能会引起代码逻辑错误(例如 :网站访问量统计功能 ServletContext(servet) Application(jsp)application。setAttbuter(“count”,100);

注意点

  1. 是单例模式的类只提供私有的构造函数;
  2. 是类定义中含有一个该类的静态私有对象;
  3. 是该类提供了一个静态的公有的函数用于创建或获取他本身的静态私有的对象

@饿汉和懒汉的区别

  • 1、类速度 懒汉类加载速度快 饿汗反之
  • 2、获取对象速度 饿汉类获取速度快 懒汉反之
  • 3、对象的生命周期 假如项目从八点到 晚8点 饿汉类项目启动的时候就被初始化啦
  • 懒汉时什么时候调用什么时候产生
    几种实现方式(示例):
    1、懒汉线程(不安全)
/**
 * @author zhangyifan
 * @version 8.0
 * @description: 线程不安全
 * @date 2021/12/2 16:01
 */
public class SlackerThreadUnsafe {
    
    
    //1私有的构造方法 防止在其他地方创建对象
   private   SlackerThreadUnsafe(){
    
    };
    //2.静态私有类实例  static 伴随类的加载而执行并且只执行一次  私有 防止该属性在其他类中被访问、
    private static SlackerThreadUnsafe instance;
    //3 公共的静态方法,返回该类的实例
    public static SlackerThreadUnsafe getInstance(){
    
    
        if (instance==null){
    
    
            instance=new SlackerThreadUnsafe();
        }
        return instance;
    }
}

/**
 * @author zhangyifan
 * @version 8.0
 * @description:
 * @date 2021/12/2 16:13
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        // new SlackerThreadUnsafe(); 不能new
        /*SlackerThreadUnsafe a=SlackerThreadUnsafe.*/
        SlackerThreadUnsafe ins1=SlackerThreadUnsafe.getInstance();
        SlackerThreadUnsafe ins2=SlackerThreadUnsafe.getInstance();
        SlackerThreadUnsafe ins3=SlackerThreadUnsafe.getInstance();
        //种常用的软件设计模式。所谓单例,就是让一个类在项目运行中只存在一个对象,即使用到这个类的地方很多,也只存在一个对象。
        System.out.println(ins1==ins2);//ture 对象一直都是一个
        System.out.println(ins3==ins2);//ture
    }
}

2、懒汉线 安全 加锁


/**
 * @author zhangyifan
 * @version 8.0
 * @description: 加锁安全的单线程
 * @date 2021/12/2 16:20
 */
public class SlackerTheadsafety {
    
    
    //1私有的构造方法 防止在其他地方创建对象
    private   SlackerTheadsafety(){
    
    };
    //2.静态私有类实例  static 伴随类的加载而执行并且只执行一次  私有 防止该属性在其他类中被访问、
    private static SlackerTheadsafety instance;
    //3 公共的静态方法,返回该类的实例   加锁 synchronized 包证线程安全
    public static synchronized SlackerTheadsafety getInstance(){
    
    
        if (instance==null){
    
    
            instance=new SlackerTheadsafety();
        }
        return instance;
    }
}

3、懒汉线 安全 双重加锁 DCL懒汉式

/**
 * @author zhangyifan
 * @version 8.0
 * @description: 双重线程锁 大大滴安全
 * @date 2021/12/2 16:26
 */
public class SlackerThreadSafetyDoubleLock {
    
    
    private SlackerThreadSafetyDoubleLock(){
    
    };

    /***
      volatile
        1.线程可见
        2.防止指令重排  在多线程情况下就有可能发生指令重拍  会发发生   singleton为null  即aaaa为null
     SlackerThreadSafetyDoubleLock    aaaa = new SlackerThreadSafetyDoubleLock();
        在jvm中           1、在堆中开辟空间(伊甸园区)
                          2、给对象属性赋值
                        3、拿aaaa引用指向该空间
                        以上3步四原子操作  执行顺序 可能是 1.2.3 或者 1.3.2.。
                            只有 1。2.3的顺序才能正常执行
                        volatile关键字的加上去 就一定是  1。2.3。。。。顺序

     */
    private volatile static SlackerThreadSafetyDoubleLock aaaa;
    //volatile关键字作用为禁止指令重排,保证返回
    //volatile只保证可见性,和有序性,不保证原子性

    public   static  SlackerThreadSafetyDoubleLock getAaaa(){
    
    

        //如果没有对象就创建对象
        if (aaaa==null){
    
    
            //假如判断为空的线程为n个 ,n个都进入上面的判断,防止三个都实例化对象
            synchronized (SlackerThreadSafetyDoubleLock.class){
    
    
                //第一个对象实例化后,防止剩下的进行实例化   //防止一个线程执行完,其他线程拿到锁,再次执行对象创建
                if (aaaa==null){
    
    
                    aaaa = new SlackerThreadSafetyDoubleLock();
                }
            }
        }
        return aaaa;
    }
    //新添加的
       public static void main(String[] args) throws Exception {
    
    
        //利用反射破解  //没有绝对的安全
        SlackerThreadSafetyDoubleLock a2=SlackerThreadSafetyDoubleLock.getAaaa();
        Constructor<SlackerThreadSafetyDoubleLock> declaredConstructors = SlackerThreadSafetyDoubleLock.class.getDeclaredConstructor(null);
        declaredConstructors.setAccessible(true);//无视
        //在Java反射机制中,通过GetDeclaredConstructor获取的私有构造进行暴力访问,如果想要访问请打开权限为[true].
        SlackerThreadSafetyDoubleLock a3 = declaredConstructors.newInstance();
        //两个对象不同
        System.out.println(a2);
        System.out.println(a3);

    }
}

但是线程的 同步锁是比较耗费资源的,如果在程序中频繁的获取对象,这样的话会大大降低效率 所以说,在单例中添加同步锁的方法比较适用于对对象获取不是很频繁地情况。

饿汉(starving)

/**
 * @author zhang yifan
 * @version 8.0
 * @description:饿汉式单例(线程安全)
 * @date 2021/12/2 16:55
 */
public class Hungry {
    
    
    /* 你用不用都创建对象 注意*/
    private Hungry(){
    
    
    }
    private final  static Hungry HUNGRY=new Hungry();//注意常量大写
    public static  Hungry getInstance(){
    
    
        return HUNGRY;
    }
}

5、饿汉静态线程安全

/**
 * @author zhangyifan
 * @version 8.0
 * @description: 静态线程安全的饿汉
 * @date 2021/12/3 9:24
 */
public class StarvingThreadSafeStatic {
    
    
    /**
     * 1、私有构造
     */
    private StarvingThreadSafeStatic(){
    
    

    }
    /**
     * 2、私有静态属性
     */
    private static StarvingThreadSafeStatic instance;

    /**
     * 匿名静态块
     */
    static {
    
    
        //需要多行代码的时候使用
      instance=new StarvingThreadSafeStatic();
    }
}

6、枚举单例模式

package org.example.dp.single;

//enum本身也是一个class类
public enum EnumSingle {
    
    
    INSTANCE;
    public EnumSingle getInstance(){
    
    //获得实例 工厂方法 获取单例
        return INSTANCE;
    }

}
class Test{
    
    
    public static void main(String[] args) {
    
    
        EnumSingle enumSingle= EnumSingle.INSTANCE;
        EnumSingle enumSingle2= EnumSingle.INSTANCE;
        System.out.println(enumSingle2);
        System.out.println(enumSingle);
    }
}

7、静态内部类

/**
 * @author zhangyifan
 * @version 8.0
 * @description:静态内部类 (实例消耗资源时,实现延迟加载)外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。
 * @date 2021/12/2 18:54
 */
public class Holder {
    
    
    private Holder (){
    
    

    }
    public static Holder getInstance(){
    
    
        return InnerClass.HOLDER;
    }//静态内部类 
    public static class InnerClass{
    
    
        private static final Holder HOLDER=new Holder();
    }
}

(实例消耗资源时,实现延迟加载)

总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

猜你喜欢

转载自blog.csdn.net/qq_45438019/article/details/121669404