Java设计模式

前言:设计模式简述

设计模式是一种编程思想,它实际就是面向对象思想的实际运用。它体现了复用性、低耦合性、高扩张性等等面向对象的思想,能大大提高我们的项目质量。

总共有23种设计模式,这些模型在大多时候是混合在一起使用的,它们各自有优点和缺点,应结合实际正确使用。

  • 创建型模式
    单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式

  • 结构式模式
    设配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

  • 行为型模式
    模板方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、访问者模式、职责链模式


创建者模式

单例模式

顾名思义,单例模式就是一个类在整个运行时期,只有一个实例对象。为什么会出现单例模式呢?比如对于某些类,它的创建和销毁过程过于复杂,且它可以复用,如果对它进行频繁的创建和销毁,就会浪费大量资源。所以单例模式应运而生。

  • 优点:可以节省不必要浪费的资源
  • 特点
    1. 类在整个运行期间都只有一个实例
    2. 构造器私有,有公开的构造实例的静态方法
    3. 有一个静态的类实例对象,表示唯一存在的实例对象

下面来看代码,单例模式有两种实现方式,分别是饿汉式懒汉式

饿汉式
public class Test {
    private Test() {
    }

    private static final Test test = new Test();

    public static Test getInstance(){
        return test;
    }
}
懒汉式
public class Test {
    private Test() {
    }

    private static Test test;

    public static Test getInstance(){
        if(test == null){
            test = new Test();
        }
        return test;
    }
}

顾名思义,饿汉式就是加载的时候就去创建实例对象,懒汉式就是在第一次使用的时候再去创建实例对象,毫无疑问,肯定是懒汉式比较优秀。不过懒汉式也有缺点,就是它是线程不安全的。

懒汉式不是线程安全的,那怎么解决呢?加锁嘛,我们可以用synchronized锁住就好。但是如果直接在getInstance()方法上加锁又对性能影响过大,所以我们可以考虑一下方法。

public class Test {
    private Test() {
    }

    private static Test test;

    public static synchronized Test getInstance(){
        if(test == null){
            synchronized (Test.class){
                test = new Test();
            }
        }
        return test;
    }
}

如上面,使用类锁便可解决,但真的解决了吗?并没有,这个代码还是有问题的,因为假如已经有多个线程进入if语句里,就会造成创建多个实例对象,从而破坏了单例模式,所以需要再加一个判断,也就是我们的双重检测锁模式的懒汉式,也叫DLC懒汉式。

public class Test {
    private Test() {
    }

    private static Test test;

    public static synchronized Test getInstance(){
        if(test == null){
            synchronized (Test.class){
                if(test == null){
                    test = new Test();
                }
            }
        }
        return test;
    }
}

还没完,如果你对多线程熟悉,你会发现test = new Test();这一步并不是原子操作,它分为3步操作,所以可能会出现指令重排,造成错误,所以应该将test对象使用volatile修饰。所以完整的懒加载且线程安全的写法如下:

public class Test {
    private Test() {
    }

    private static volatile Test test;

    public static synchronized Test getInstance(){
        if(test == null){
            synchronized (Test.class){
                if(test == null){
                    test = new Test();
                }
            }
        }
        return test;
    }
}

但是但是,还是没完,以上的写法其实都是可以通过反射来破坏单例模式的,对此我们也有对策,就是采用枚举类型,枚举类型是无法通过反射来破坏的,它会报出异常。但是枚举类型又不能用于懒汉式,它的原理和饿汉式相同,类加载的时候就已经构建好实例对象了。

所以当前暂时没有同时满足懒汉式、线程安全、反射不能破坏的写法,只能满足其中的两种,这就是单例模式,如果有兴趣也可以看下以下两个视频:

https://www.bilibili.com/video/BV1pt4y1X7kt?from=search&seid=16397970082293022209
https://www.bilibili.com/video/BV1K54y197iS?from=search&seid=16397970082293022209

工厂模式

还是先说说为什么要有工厂模式吧。工厂模式是为了解耦:把对象的创建和使用的过程分开。就是Class A 想调用 Class B ,那么A只是调用B的方法,而至于B的实例化,就交给工厂类。

其次,工厂模式可以降低代码重复。如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。

同理由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。

下面有一篇博客,个人觉得写得很不错,对于了解工厂模式的作用有很大帮助:

https://blog.youkuaiyun.com/kocscs123/article/details/53243847?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159594280519724811852397%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=159594280519724811852397&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v3~pc_rank_v2-1-53243847.first_rank_ecpm_v3_pc_rank_v2&utm_term=%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F%E4%BD%9C%E7%94%A8&spm=1018.2118.3001.4187

工厂模式分为两种:简单工厂模式和工厂方法模式,它们的核心本质就是实例化对象不直接new出来,而是使用工厂方法创建,正如我们熟知的一些类有Factory,如线程池。这样实现了创建者和调用者的分离。

  • 优点:降低了耦合度,使创建者和使用者分离
  • 分类
    • 简单工厂模式:用来生产同一等级结构中的任意产品,代码量较小,但增加新产品需要修改已有代码
    • 工厂方法模式:用来生产同一等级结构中的固定产品,代码量较大,但支持增加新产品,只需在原有基础上扩展即可

下面举例来演示下这两种方法,用买车的例子。假如有两种车,分别为AB种,它们实现了car这个接口,现在有用户来买车:

简单工厂模式

在这里插入图片描述

public interface Car {
    void name();
}

class A implements Car{
    @Override
    public void name() {
        System.out.println("A种车");
    }
}

class B implements Car{
    @Override
    public void name() {
        System.out.println("B种车");
    }
}

class Factory{
    public static A getA(){
        return new A();
    }

    public static B getB(){
        return new B();
    }
}

class  Customer{
    public static void main(String[] args) {
        //不直接new对象,而是通过工厂创建实例对象
        A a = Factory.getA();
        a.name();
        B b = Factory.getB();
        b.name();
    }
}

上面便是简单工厂模式实现,但是如果我们新增一种车,则必定要改动工厂的代码,不符合我们的面向对象思想。下面我们来看下工厂方法模式:

工厂方法模式

在这里插入图片描述

public interface Car {
    void name();
}

class A implements Car{
    @Override
    public void name() {
        System.out.println("A种车");
    }
}

class B implements Car{
    @Override
    public void name() {
        System.out.println("B种车");
    }
}

interface CarFactory{
     Car getInstance();
}

class ACarFactory implements CarFactory{

    @Override
    public Car getInstance() {
        return new A();
    }
}

class BCarFactory implements CarFactory{
    @Override
    public Car getInstance() {
        return new B();
    }
}

class  Customer{
    public static void main(String[] args) {
        //不直接new对象,而是通过工厂创建实例对象
        A a = (A)new ACarFactory().getInstance();
        a.name();
        B b = (B)new BCarFactory().getInstance();
        b.name();
    }
}

从上面我们可以很清楚的看到,如果我们有了一种新的车,我们只需要新建一个新的工厂然后实现工厂接口就可以了,且各种车的工厂是独立的,我们也可以在各种车工厂里提供特有方法,但是弊端也很明显,就是代码量大大增加,每多一种车就要多出一个工厂。所以我们要根据实际情况决定。

抽象工厂模式

在IT领域,尤其是地理信息系统(GIS)中,坐标转换是一项关键技术。本文将深入探讨百度坐标系、火星坐标系和WGS84坐标系之间的相互转换,并介绍如何使用相关工具进行批量转换。 首先,我们需要了解这三种坐标系的基本概念。WGS84坐标系,即“World Geodetic System 1984”,是一种全球通用的地球坐标系统,广泛应用于GPS定位和地图服务。它以地球椭球模型为基础,以地球质心为原点,是国际航空和航海的主要参考坐标系。百度坐标系(BD-09)是百度地图使用的坐标系。为了保护隐私和安全,百度对WGS84坐标进行了偏移处理,导致其与WGS84坐标存在差异。火星坐标系(GCJ-02)是中国国家测绘局采用的坐标系,同样对WGS84坐标进行了加密处理,以防止未经授权的精确位置获取。 坐标转换的目的是确保不同坐标系下的地理位置数据能够准确对应。在GIS应用中,通常通过特定的算法实现转换,如双线性内插法或四参数转换法。一些“坐标转换小工具”可以批量转换百度坐标、火星坐标与WGS84坐标。这些工具可能包含样本文件(如org_xy_格式参考.csv),用于提供原始坐标数据,其中包含需要转换的经纬度信息。此外,工具通常会附带使用指南(如重要说明用前必读.txt和readme.txt),说明输入数据格式、转换步骤及可能的精度问题等。x86和x64目录则可能包含适用于32位和64位操作系统的软件或库文件。 在使用这些工具时,用户需要注意以下几点:确保输入的坐标数据准确无误,包括经纬度顺序和浮点数精度;按照工具要求正确组织数据,遵循读写规则;注意转换精度,不同的转换方法可能会产生微小误差;在批量转换时,检查每个坐标是否成功转换,避免个别错误数据影响整体结果。 坐标转换是GIS领域的基础操作,对于地图服务、导航系统和地理数据分析等至关重要。理解不同坐标系的特点和转换方法,有助于我们更好地处
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值