常用设计模式全面总结版(JavaKotlin)

这篇文章主要是针对之前博客的下列文章的总结版本:

  • 《设计模式系列学习笔记》
  • 《Kotlin核心编程》笔记:设计模式
  • 【Android知识笔记】FrameWork中的设计模式

主要为了在学习了 Kotlin 之后,将 Java 的设计模式实现与 Kotin 的实现放在一起做一个对比。

一、创建型模式

单例模式(Java)

Double Check Lock + volatile 版本:

public class Singleton { /** 使用 volatile 保证对该变量的写入对另一个线程可见 */private volatile static Singleton mSingleton = null;private Singleton() {}public static Singleton getInstance() {if (mSingleton == null) {synchronized(Singleton.class) {if (mSingleton == null) {mSingleton = new Singleton();}}}return mSingleton;}
}

为什么要 Double Check Lockvolatile,因为 mSingleton = new Singleton() 这一句翻译成字节码指令可能对应多条,它不是一次性完成的原子性操作,会分成三个步骤完成:

  1. 给实例分配内存,
  2. 调用构造函数初始化实例,
  3. 给变量赋值操作。

这三个步骤中后面两步可能发生指令重排序,即顺序为1-3-2,从而导致双重锁定失效,另一个线程同时访问就会拿到一个还没来得及初始化完毕的对象实例,出现问题。

volatile 在这里的主要作用是禁止指令重排序。另外 volatile 还可以保证线程可见性(但不保证原子性)。

静态内部类版本:

public class Singleton {private Singleton(){}/*** 静态内部类,内部类的实例与外部类的实例没有绑定关系,* 只有被调用到时才会加载,从而实现延迟加载*/private static class SingletonHolder {/** 静态初始化器,由 JVM 类加载过程来保证线程安全 */private static Singleton instance = new Singleton();}public static Singleton getInstance() {return SingletonHolder.instance;}
}

注意这个写法,只有getInstance()方法是public的,其他静态内部类及其内部静态变量都是private的。

getInstance方法第一次被调用的时候,会导致JVM 虚拟机加载 SingletonHolder 类,它的静态成员变量得到初始化;从而创建 Singleton 的实例。

补充:JVM加载类的过程:加载 -> 验证 -> 准备 -> 解析 -> 初始化

准备阶段会为SingletonHolder中的静态成员instance赋值为null,在编译时会收集静态赋值语句等组成类构造器<cinit>(),在初始化阶段会执行类构造器来初始化一个类。

触发 JVM 初始化一个类有以下几种情况:

  • 使用new关键字创建对象
  • 访问类的静态成员变量 或 对类的静态成员变量进行赋值
  • 调用类的静态方法
  • 反射调用类时,如 Class.forName()
  • 初始化子类时,会先初始化其父类(如果父类还没有进行过初始化的话)
  • 遇到启动类时,如果一个类被标记为启动类(即包含main方法),虚拟机会先初始化这个主类。
  • 实现带有默认方法的接口的类被初始化时(拥有被default关键字修饰的接口方法的类)
  • 使用 JDK7 新加入的动态语言支持时 MethodHandle

显然当调用Singleton.getInstance() -> SingletonHolder.instance 时,命中上面第 2 条,JVM 就会执行 SingletonHolder 这个类的初始化,然后调用其类构造器<cinit>(),这个时候就会初始化Singleton instance静态成员对象的实例。

在这个过程中, JVM 会保证SingletonHolder这个类的<clinit>()方法被正确的加锁同步,在多线程访问的情况下,只会有一个线程看到这个类的初始化过程,而其他线程是不可见的, 其它线程都需要等待,直到<clinit>()方法执行完毕。所以是线程安全的。

这种方式既保证了线程安全性,又保证了对象的唯一性。并且还是懒加载,只有被调用时才会初始化。

枚举单例:

public enum Singleton {INSTANCE;public void doSomething() {// todo}
}

Android源码中的单例:

  • context.getSystemService(name)context的实现类 ContextImpl 中使用静态的Map对象存储ServiceFecther对象(恶汉)
  • 线程内的单例ThreadLocal,如 ChoreographerLooper 中都使用 ThreadLocal 绑定当前线程,同一个 ThreadLocal 对象在同一个线程内返回的对象是唯一的。
  • 进程内的单例:如 ActivityManager 类的 getService() 方法返回的 IActivityManagerSingleton 单例在进程内唯一。
  • 进程间的单例ServiceManager,不同进程之间内存是隔离的,在进程间共享的单例其实是由binder驱动来保证不同的进程共用同一个对象的。对所有的进程而言,ServiceManager对应同一个binder句柄(通过 0Handle句柄引用),binder句柄处理完不同进程的请求后会转发给ServiceManager所在的进程。

Q:怎样在应用里做一个跨进程的单例?

  • 单例对象类必须实现Parcelable接口,成员变量除了基本数据类型外都必须实现Parcelable接口
  • 通过 AIDL 接口, 拿到远端进程的IBinder对象,再进行 Binder 调用远端接口的业务方法,将单例对象作为参数发布给远端进程
  • 远端进程在业务方法中将接受到单例对象保存下来使用

单例模式(kotlin)

在 kotlin 中 object 类是天生的单例模式。

object Singleton {var x: Int = 2 fun y() { }
}

这个代码翻译成 Java 字节码后发现它其实就是恶汉模式的单例

如果是一个普通类想生成单例,可以使用伴生对象 companion object 来生成:

class Singleton {companion object {var x: Int = 2fun y() { }}
}

如果要实现 Java 中静态内部类版本的单例模式,可以像下面这样写:

class Singleton private constructor() {private object Holder {val INSTANCE = Singleton()}companion object {val instance = Holder.INSTANCE}
}

object不能定义构造函数,但可以定义init块:

object Singleton {var x: Int = 2 fun y(){ }init {//object不能定义构造函数,但可以定义init块}
}

如果要在Java中使用kotlin的单例,最好在成员属性和成员方法上分别加上 @JvmField@JvmStatic注解:

object Singleton {@JvmField var x: Int = 2  @JvmStatic fun y() { } 
}
  • @JvmField 的作用是生成java的静态成员 不会生成gettersetter方法。
  • @JvmStatic 的作用是生成java的静态方法。

object修饰的类内部方法相当于静态方法,但是伪静态,也就是内部会生成一个静态的成员对象,对类的方法调用实际上是调用的内部静态成员对象的方法,只有在方法上添加@JvmStatic才会真正的生成静态方法。

普通kotlin类(非单例)中使用使用JvmFieldJvmStatic

class Foo {//普通类中使用JvmField和JvmStaticcompanion object {@JvmField var x: Int = 2@JvmStatic fun y(){  }}
}

注意,加的注解@JvmField@JvmStatic只针对java平台的可用,其他平台并不可行。

单例的object类仍可以继承类:

object Singleton: Runnable{override fun run() {}
}

工厂方法模式(Java)

  • 定义了一个创建对象的接口,但是由子类来决定要实例化的是哪一个类。工厂方法使类的实例化延迟到子类中。
public abstract class Product {public void method1() {}public abstract void method();
}
public class ConcreteProductA extends Product {@Overridepublic void method2() {//ProductA的业务处理}
}
public class ConcreteProductB extends Product {@Overridepublic void method2() {//ProductB的业务处理}
}
public abstract class Factory {/** * 此方法必须返回一个Product类型的对象*/public abstract <T extends Product> T createProduct(Class<T> c);
}
public class ConcreteFactory extends Factory { @Overridepublic <T extends Product> T createProduct(Class<T> c) {//创建Product的逻辑,这里是直接调用Class的newInstance方法Product product = null;try {product = (Product) Class.forName(c.getName()).newInstance();} catch (Exception e) {e.printStackTrace();}return (T)product;}
}
public class Client {public static void main(String[] args) {Creator creator = new ConcreteCreator();Product productA = creator.createProduct(ConcreteProductA.class);Product productB = creator.createProduct(ConcreteProductB.class);......}
}

简单工厂模式:如果实际当中只需要一个工厂类,那么就不需要抽象的工厂基类,可以把创建产品的方法改为静态方法

public class HumanFactory {@Overridepublic static <T extends Human> T createHuman(Class<T> c) {Human human = null;try {human = (Human) Class.forName(c.getName()).newInstance();} catch (Exception e) {e.printStackTrace();}return (T) human;}
}

多个工厂类:在实际当中如果初始化创建对象的过程比较复杂,比如不同的产品可能会设置不同的参数,这个时候创建产品的方法不能共用,所以这时就需要为每一个产品类创建一个工厂类。

可生成单例模式的工厂类:既然工厂方法是用来生产对象的,那么就可以对工厂方法做简单的修改,只返回一个对象,就变成了单例模式。

public class Singleton {//不允许通过new产生对象private Singleton(){}public void doSomething() {//业务处理}
}public class SingletonFactory {private static Singleton singleton;static {try {Class<?> clazz = Class.forName(Singleton.class.getName());//获取无参的构造函数Constructor<?> constructor = clazz.getDeclaredConstructor();//设置无参的构造函数可访问constructor.setAccessible(true);//创建一个实例对象singleton = (Singleton) constructor.newInstance();} catch (Exception e) {e.printStackTrace();}}public static Singleton getSingleton() {return singleton;}
}

可复用缓存的工厂类:如果创建对象的过程比较复杂,或者非常耗时,可以在工厂类内部对已创建的对象进行缓存,以备下次使用。

public class ProductFactory {private static final Map<String, Product> productMap = new HashMap<>();public static synchronized Product createProduct(String type) {Product product = null;//如果缓存中有该对象实例直接使用if (productMap.containsKey(type)) {product = productMap.get(type);} else {if (type.equals("ProductA")) {product = new ConcreteProductA();} else {product = new ConcreteProductB();}//把创建的对象缓存起来productMap.put(type, product);}return product;}
}

Android源码中的工厂方法模式:

  • 集合类的iterator()返回的是一个新的迭代器对象,其实就是一个工厂方法。
  • ActivityonCreate()方法某种角度上可以看作是一个用于创建ContentView的工厂方法,该ContentView用于填充PhoneWindowDecorView的内容区。

抽象工厂模式(Java)

  • 为创建一组相关或依赖的对象提供一个接口,而无需指定它们的具体类。

在这里插入图片描述

/**
* 抽象工厂
*/
public interface AbstractFactory {/**创建A产品家族的产品*/public AbstractProductA createProductA();/**创建B产品家族的产品*/public AbstractProductB createProductB();
}
public class ConcreteFactory1 implements AbstractFactory {@Overridepublic AbstractProductA createProductA() {//创建一个来自A产品家族的产品return new ProductA1();}@Overridepublic AbstractProductB createProductB() {//创建一个来自B产品家族的产品return new ProductB1();}
}
public class ConcreteFactory2 implements AbstractFactory {@Overridepublic AbstractProductA createProductA() {//创建一个来自A产品家族的产品return new ProductA2();}@Overridepublic AbstractProductB createProductB() {//创建一个来自B产品家族的产品return new ProductB2();}
}
/**
* 产品A家族
*/
public abstract class AbstractProductA {/**每个产品共有的方法*/public void shareMethod() {}/**每个产品不同实现的方法*/public abstract void doSomething();
}
public class ProductA1 extends AbstractProductA {@Overridepublic void doSomething() {System.out.println("产品A1的实现方法");}
}
public class ProductA2 extends AbstractProductA {@Overridepublic void doSomething() {System.out.println("产品A2的实现方法");}
}
/**
* 产品B家族
*/
public abstract class AbstractProductB {/**每个产品共有的方法*/public void shareMethod() {}/**每个产品不同实现的方法*/public abstract void doSomething();
}
public class ProductB1 extends AbstractProductB {@Overridepublic void doSomething() {System.out.println("产品B1的实现方法");}
}
public class ProductB2 extends AbstractProductB {@Overridepublic void doSomething() {System.out.println("产品B2的实现方法");}
}
public class Client {public static void main(String[] args) {//创建两个工厂AbstractFactory factory1 = new ConcreteFactory1();AbstractFactory factory2 = new ConcreteFactory2();//使用factory1来创建一组产品,它们来自不同的产品家族AbstractProductA productA1 = factory1.createProductA();AbstractProductB productB1 = factory1.createProductB();//使用factory2来创建一组产品,它们来自不同的产品家族AbstractProductA productA2 = factory2.createProductA();AbstractProductB productB2 = factory2.createProductB();//do something...}
}

抽象工厂模式其实就是每个工厂类是为生产某一类相关性很强的产品类族专门特供的版本

在这里插入图片描述

工厂方法模式(Kotlin)

用单例代替工厂类

我们已经知道 Kotlin 支持用 object 来实现 Java 中的单例模式。所以我们可以使用一个 object 单例来代替一个工厂类。

object ComputerFactory { // 用 object 代替 classfun produce(type: ComputerType): Computer {return when (type) {ComputerType.PC -> PC()ComputerType.Server -> Server()}}
}
fun main() {val compter = ComputerFactory.produce(ComputerType.PC)println(compter.cpu)
}

我们可以通过operator操作符重载invoke方法来代替produce,从而进一步简化表达:

object ComputerFactory {   operator fun invoke(type: ComputerType): Computer {return when (type) {ComputerType.PC -> PC()ComputerType.Server -> Server()}}
}
fun main() {val compter = ComputerFactory(ComputerType.PC)println(compter.cpu)
}

伴生对象创建静态工厂方法

interface Computer {val cpu: Stringcompanion object {operator fun invoke(type: ComputerType): Computer {return when (type) {ComputerType.PC -> PC()ComputerType.Server -> Server()}}}
}class PC(override val cpu: String = "Core") : Computer
class Server(override val cpu: String = "Xeon") : Computerenum class ComputerType { PC, Server }fun main() {val compter = Computer(ComputerType.PC)println(compter.cpu)
}

这样就可以在一个接口类中添加伴生对象的方式来创建静态工厂方法。

伴生对象也可以指定名字:

interface Computer {val cpu: Stringcompanion object Factory {operator fun invoke(type: ComputerType): Computer {return when (type) {ComputerType.PC -> PC()ComputerType.Server -> Server()}}}
}fun main() {val compter = Computer.Factory(ComputerType.PC)println(compter.cpu)
}

为伴生对象添加扩展方法

主要是针对三方类库中无法直接修改源码的类。

fun Computer.Companion.fromCPU(cpu: String): ComputerType? = when(cpu) {"Core" -> ComputerType.PC"Xeon" -> ComputerType.Serverelse -> null
}

指定伴生对象名字的写法:

fun Computer.Factory.fromCPU(cpu: String): ComputerType? = when(cpu) {"Core" -> ComputerType.PC"Xeon" -> ComputerType.Serverelse -> null
}

调用:

fun main() {val type = Computer.fromCPU("Core")println(type)
}

抽象工厂模式(Kotlin)

Kotlin 中的可以使用内联函数 inline + reified 关键字来简化抽象工厂模式:

class Dell: Computer { }
class Asus: Computer { }
class Acer: Computer { }class DellFactory: AbstractFactory() {override fun produce() = Dell()
}
class AsusFactory: AbstractFactory() {override fun produce() = Asus()
}
class AcerFactory: AbstractFactory() {override fun produce() = Acer()
}abstract class AbstractFactory {abstract fun produce(): Computercompanion object {inline operator fun <reified T : Computer> invoke(): AbstractFactory = when(T::class) {Dell::class -> DellFactory()Asus::class -> AsusFactory()Acer::class -> AcerFactory()else -> throw IllegalArgumentException()}}
}fun main() { val dellFactory = AbstractFactory<Dell>()val dell = dellFactory.produce()println(dell)
}

建造者模式(Java)

  • 封装一个产品的构建过程,并允许按步骤构造。
  • 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
public class Robot {private final String code;private final String battery;private final Integer height;private final Integer weight;private Robot(String code, String battery, Integer height, Integer weight) {this.code = code;this.battery = battery;this.height = height;this.weight = weight;}public static class Builder {private final String code;private String battery;private Integer height;private Integer weight;public Builder(String code) {this.code = code;}public Builder setBattery(String battery) {this.battery = battery;return this;}public Builder setHeight(int height) {this.height = height;return this;}public Builder setWeight(int weight) {this.weight = weight;return this;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/588781.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

以太网二层交换机实验

实验目的&#xff1a; &#xff08;1&#xff09;理解二层交换机的原理及工作方式&#xff1b; &#xff08;2&#xff09;利用交换机组建小型交换式局域网。 实验器材&#xff1a; Cisco packet 实验内容&#xff1a; 本实验可用一台主机去ping另一台主机&#xff0c;并…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的图像剪切(ROI)功能(C++)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK设置相机的图像剪切&#xff08;ROI&#xff09;功能&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的图像剪切&#xff08;ROI&#xff09;功能的技术背景CameraExplorer如何使用图像剪切&#xff08;ROI&#xff09;功…

Python武器库开发-武器库篇之Git的分支使用(三十九)

武器库篇之Git的分支使用(三十九) Git分支是一种用于在项目中并行开发和管理代码的功能。分支允许开发人员在不干扰主要代码的情况下创建新的代码版本&#xff0c;以便尝试新功能、修复错误或独立开发功能。一般正常情况下&#xff0c;开发人员开发一个软件&#xff0c;会有两…

HarmonyOS4.0系统性深入开发07创建一个ArkTS卡片

创建一个ArkTS卡片 在已有的应用工程中&#xff0c;创建ArkTS卡片&#xff0c;具体操作方式如下。 创建卡片。 根据实际业务场景&#xff0c;选择一个卡片模板。 在选择卡片的开发语言类型&#xff08;Language&#xff09;时&#xff0c;选择ArkTS选项&#xff0c;然后单…

nodejs+vue+微信小程序+python+PHP技术的健康信息网站-计算机毕业设计推荐

3.2 功能性需求分析 健康信息网站为会员提供健康信息服务的系统&#xff0c;管理员通过登录系统&#xff0c;管理会员信息、健康咨询、健康知识、健康档案、健康养生、健康信息的搜索、健康资讯等。需要学习的会员浏览健康信息网站&#xff0c;查询所有的健康信息&#xff0c;可…

【Java EE初阶三 】线程的状态与安全(下)

3. 线程安全 线程安全&#xff1a;某个代码&#xff0c;不管它是单个线程执行&#xff0c;还是多个线程执行&#xff0c;都不会产生bug&#xff0c;这个情况就成为“线程安全”。 线程不安全&#xff1a;某个代码&#xff0c;它单个线程执行&#xff0c;不会产生bug&#xff0c…

七:Day01_Java9—16新特性

第一章 JDK9 新特性 jdk9是新特性最多的&#xff0c;因为jdk8是一个稳定版本。 1、JDK9新特性概述 模块系统 &#xff08;Module System&#xff09; Java9最大特性。它提供了类似于OSGI框架的功能&#xff0c;模块之间存在相互的依赖关系&#xff0c;可以导出一个公共的API…

YOLOv8改进 | 2023注意力篇 | iRMB倒置残差块注意力机制(轻量化注意力机制)

一、本文介绍 本文给家大家带来的改进机制是iRMB&#xff0c;其是在论文Rethinking Mobile Block for Efficient Attention-based Models种提出&#xff0c;论文提出了一个新的主干网络EMO(后面我也会教大家如何使用该主干&#xff0c;本文先教大家使用该文中提出的注意力机制…

【Java进阶篇】什么是UUID,能不能保证唯一?

什么是UUID&#xff0c;能不能保证唯一? ✔️典型解析✔️优缺点 ✔️各个版本实现✔️V1.基于时间戳的UUID✔️V2.DCE(Distributed Computing Environment)安全的UUID✔️V3.基于名称空间的UUID(MD5)✔️V4.基于随机数的UUID✔️V5.基于名称空间的UUID(SHA1)✔️各个版本总结…

学生管理系统(vue + springboot)

学生管理系统&#xff08;vuespringboot&#xff09;资源-CSDN文库 项目介绍 这是一个采用前后端分离开发的项目&#xff0c;前端采用 Vue 开发、后端采用 Spring boot Mybatis 开发。 项目部署 ⭐️如果你有 docker 的话&#xff0c;直接 docker compose up 即可启动&#…

SpringBoot入门指南(学习笔记)

概述 Springboot是Spring的一个子项目&#xff0c;用于快速构建Spring应用程序 入门 ①创建SpringBoot工程 ②编写Controller RestController public class HelloContoller {RequestMapping("/hello")public String hello() {return "hello";} }③运行…

golang锁源码【只有关键逻辑】

条件锁 type Cond struct {L Lockernotify notifyList } type notifyList struct {wait uint32 //表示当前 Wait 的最大 ticket 值notify uint32 //表示目前已唤醒的 goroutine 的 ticket 的最大值lock uintptr // key field of the mutexhead unsafe.Pointer //链表头…

论文解读:Coordinate Attention for Efficient Mobile Network Design(CVPR2021)

论文前言 原理其实很简单&#xff0c;但是论文作者说得很抽象&#xff0c;时间紧的建议直接看3.1中原理简述CBMA、原理简述CBMA以及3.2中原理简述coordinate attention block即可。 Abstract 最近关于mobile network设计的研究已经证明了通道注意(例如&#xff0c;the Squee…

23. 一维数组

写在前面&#xff1a; 今天是2023年12月31日&#xff0c;也是整个2023年的最后一天。我在CSDN上只有短短几个月的时光&#xff0c;但非常感谢大家的支持&#xff0c;作为一名刚刚大一的大学生呢&#xff0c;学习编程&#xff0c;学习写博客是很重要的事&#xff0c;所以在新的…

翻页的电子画册如何制作

​在过去&#xff0c;一本精美的画册往往需要大量的人力物力去印刷、装帧、运输。而现在&#xff0c;只需一台电脑、一个网址和一个创意&#xff0c;就可以轻松制作出一本电子画册。这种变化不仅降低了成本&#xff0c;还带来了更多的便利性和灵活性。 首先&#xff0c;你需要选…

网络故障排查和流量分析利器-Tcpdump命令

Tcpdump是一个在Unix/Linux系统上广泛使用的命令行网络抓包工具。它能够捕获经过网络接口的数据包&#xff0c;并将其以可读的格式输出到终端或文件中。Tcpdump是一个强大的命令行工具&#xff0c;能够捕获和分析网络数据包&#xff0c;为网络管理员和安全专业人员提供了深入了…

【网络面试(6)】IP协议对网络包的转发

在前面的博客中&#xff0c;我们提到过&#xff0c;网络传输的报文是有真实的数据包和一些头部组成&#xff0c;目前我们了解的头部就有TCP头、IP头、MAC头&#xff0c;而且这三个头部信息都是在应用程序委托给协议栈之后&#xff0c;被写入的相关信息&#xff0c;这些头部都是…

修改jenkins的目录(JENKINS_HOME)

默认JENKINS_HOME是/var/lib/jenkins/ 现要修改为/home/jenkins_data/jenkins 最开始 sudo cp -a /var/lib/jenkins/ /home/jenkins_data/ 然后如下操作&#xff1a; 1、首先 /etc/sysconfig/jenkins&#xff1a;jenkins配置文件&#xff0c;“端口”&#xff0c;“JENKIN…

08-接口文档管理工具-项目集成knife4j__ev

2、knife4j快速入门 2.1 knife4j介绍 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案,前身是swagger-bootstrap-ui,取名kni4j是希望它能像一把匕首一样小巧,轻量,并且功能强悍! gitee地址&#xff1a;knife4j: Knife4j是一个集Swagger2 和 OpenAPI3为一体的增…

第二十六周:学习笔记

第二十六周&#xff1a;学习笔记 摘要Abstract全卷积网络 FCN1. CNN 与 FCN2. 全连接层 --> 成卷积层3. FCN的缺点 摘要 全卷积神经网络&#xff08;Fully Convolutional Network&#xff0c;FCN&#xff09;是一种用于图像分割和语义分割任务的深度学习模型。与传统的卷积…