为什么说静态工厂方法可能会比构造器更好?听说读完就会有一个女朋友?

2024-06-15 07:32

本文主要是介绍为什么说静态工厂方法可能会比构造器更好?听说读完就会有一个女朋友?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

    • 什么是静态工厂方法
    • 静态工厂方法比构造器好在哪里
      • 静态工厂方法有名称而构造器没有(前者可读性更好)
      • 不必每次调用它们的时候都创建一个新对象
      • 可以返回原返回类型的任何子类对象
      • 创建泛型对象可以更加简洁
    • 静态工厂的局限之处
    • 总结

前言:相信读过《Effective Java》(第二版)的小伙伴知道我在说什么,作者在书中总结了78条黄金建议,其中建议的第一条就是:考虑用静态工厂方法替代构造器。可以说是开幕雷击,第一条我就懵X了,什么是静态工厂方法?为什么要替代构造器?是构造器不香吗?本文将根据这些疑问刨根问底,详细分析静态工厂方法的优劣,相信我,有问题欢迎评论指出,本篇文章一定不会枯燥,坚持读完哦~

在这里插入图片描述

什么是静态工厂方法

当我们想要获得一个类的实例对象的时候,恐怕一下子就通过new一个对象来获得了。那么静态工厂方法是怎么实现的呢?其实也很简单:

public static Girl newGirlFriend(boolean isBeautifulGirl) {return new Girl(isBeautifulGirl);
}

如此,我们通过类名加newGirlFriend就可以轻松地拿到对象。

所谓的静态工厂方法就是不通过new,而通过一个静态方法返回自身的实例对象

静态工厂方法比构造器好在哪里

由《Effective Java》可以得到四点静态工厂方法比构造器好的地方:
①静态工厂方法有名称而构造器没有(前者可读性更好)
②不必每次调用它们的时候都创建一个新对象
③可以返回原返回类型的任何子类对象(不局限于单一特定的类)
④创建泛型对象可以更加简洁

静态工厂方法有名称而构造器没有(前者可读性更好)

说到有名称这点,构造器是真的没法儿比:

public class GirlFriend {private boolean isBeautiful;private boolean isTall;public GirlFriend(){}public GirlFriend(boolean isBeautiful) {this.isBeautiful = isBeautiful;    }public GirlFriend(boolean isBeautiful, boolean isTall) {this.isBeautiful = isBeautiful;    this.isTall = isTall;    }
}

调用者如果不是非常熟悉这段代码,必须要看文档中有哪些重载的构造方法,稍有不慎,就会调错,毕竟构造方法都是一个名字,只有参数不同而已。但是静态工厂方法就不一样了,可以起很多能让调用者见名知意的名字:getBeautifulGirlfriend(); getTallGirl();,是不是看出差距了

另外:正是因为这一特性,所以哪怕他们有相同的参数,也不会报错,如果构造器有两个构造方法含有相同参数,必定出错,但是任性的静态工厂方法想怎么起名字就怎么起名字,完美避过了这个坑~
在这里插入图片描述

不必每次调用它们的时候都创建一个新对象

当我们需要获得类的实例对象的时候,为了性能,我们不在乎他是不是新对象,使用new必然会得到一个不同地址的对象(姑且称之为新对象),当我们使用new GirlFriend();这种方式直接创建对象,相当于对JVM说,我要一个对象,然后JVM会去根据双亲委派原则加载验证准备解析(new一个对象发生了什么?),所以开支是蛮大的,尤其在重复新建一个对象的时候。小斌哥我之前的代码就都是这种毛病,并没有使用单例模式,每个方法都new同一个对象,我去,简直是伤害了我的CPU,心疼我的旧电脑,怪不得那么快就挂了(玩笑话);使用静态工厂方法最简单的一个例子就是单例模式,我们可以确保获取多个对象的时候只需要创建一次对象并且销毁一次对象,这样就很“环保”了,对内存的开支会达到最小,这点是构造器望尘莫及的。

说到单例模式,大家会想到“饿汉式”与“懒汉式”,举个最简单的例子不考虑线程安全给大家展示一下单例模式是如何做到多次获取对象只创建并销毁一次:
饿汉式:

public class Singleton{private static Singleton singleton = new Singleton ();private Singleton (){}public static Singleton getInstance(){return singleton;
}

懒汉式:

public class Singleton{private static Singleton singleton = null;public static Singleton getInstance(){if(singleton==null){singleton = new Singleton();}return singleton;}
}

使用单例模式不管创建多少次都会返回相同的对象,所以相当于对象被多次循环利用了,提升了效率。在这里插入图片描述

可以返回原返回类型的任何子类对象

这体现了里氏原则:任何基类可以出现的地方,子类一定可以出现。 比如有这样的UML:
在这里插入图片描述

public static Singleton getGirlFriend(){return new 女朋友();//可以返回新垣结衣()、长泽雅美()、松冈茉优()、绫濑遥()//因为都是女朋友的子类
}

这个优点有些欺负构造器了,毕竟它没有返回值。

创建泛型对象可以更加简洁

作者想表示过去泛型书写比较复杂,比如:

Map<String, List<String>> map = new HashMap<String, List<String>>(); 

额,但是jdk1.7之后这个问题已经木有了,毕竟这本书有一定年头了(第二版参考的是jdk1.6),所以就当这条没有吧。

另外,静态工厂方法还有其他好处,比如可以隐藏实现细节,只提供用户最简单的方法入口,比如让你写一个计算器软件,用户只需要输入:3*(7/1.455)+11-12,而方法的具体实现细节是私有的,用户是看不到的,也不需要看到,否则就体现不了封装的特性了,一个是用户使用着麻烦,另外是出错的可能性更大,你暴露给用户更多的参数更多的方法,出BUG的可能性就更大。这就好比现在的手机,只有关机键、音量键,如果把手机后盖拆开暴露给你,手机被整坏的可能性必然会提升啊。。

我们熟知的工具类Collections就是一个很好的例子,他把构造器设置为私有,所以调用者不能通过new来调用类中的方法,只能通过类名加方法名去调用,并且内部需要计算的方法都设为private,调用者看不到也不需要去知道,比如sort方法和binarySearch等。

静态工厂的局限之处

凡事都是两面的,虽然静态工厂方法很好,但是它只要构造方法私有化了,就不能被实例化,也就是说,想让拒绝访问构造器的类拥有子类是不可能的(就像做了绝育手术)。第二点就是它跟普通的静态方法没啥两样,虽然我们管它叫静态工厂方法,但是它依然只是一个很简单的静态方法而已,并不像构造器,有自己的专属名称,还可以在API文档中看到他的名字。但是静态工厂方法也会有一些不成文的规定,正如我们会看到一些newInstance()、valueOf()、getInstance()等,这些往往就是静态工厂方法实现的。

总结

作者给我们这条建议,并非让我们用静态构造方法彻底替换构造器,只是希望我们在以后可以有多一种选择,直接new并不是唯一的方法,他们各有各的好,需要我们去进行合适的选择。

这篇关于为什么说静态工厂方法可能会比构造器更好?听说读完就会有一个女朋友?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062813

相关文章

golang中reflect包的常用方法

《golang中reflect包的常用方法》Go反射reflect包提供类型和值方法,用于获取类型信息、访问字段、调用方法等,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值... 目录reflect包方法总结类型 (Type) 方法值 (Value) 方法reflect包方法总结

C# 比较两个list 之间元素差异的常用方法

《C#比较两个list之间元素差异的常用方法》:本文主要介绍C#比较两个list之间元素差异,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1. 使用Except方法2. 使用Except的逆操作3. 使用LINQ的Join,GroupJoin

MySQL查询JSON数组字段包含特定字符串的方法

《MySQL查询JSON数组字段包含特定字符串的方法》在MySQL数据库中,当某个字段存储的是JSON数组,需要查询数组中包含特定字符串的记录时传统的LIKE语句无法直接使用,下面小编就为大家介绍两种... 目录问题背景解决方案对比1. 精确匹配方案(推荐)2. 模糊匹配方案参数化查询示例使用场景建议性能优

关于集合与数组转换实现方法

《关于集合与数组转换实现方法》:本文主要介绍关于集合与数组转换实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、Arrays.asList()1.1、方法作用1.2、内部实现1.3、修改元素的影响1.4、注意事项2、list.toArray()2.1、方

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

一文详解Git中分支本地和远程删除的方法

《一文详解Git中分支本地和远程删除的方法》在使用Git进行版本控制的过程中,我们会创建多个分支来进行不同功能的开发,这就容易涉及到如何正确地删除本地分支和远程分支,下面我们就来看看相关的实现方法吧... 目录技术背景实现步骤删除本地分支删除远程www.chinasem.cn分支同步删除信息到其他机器示例步骤

在Golang中实现定时任务的几种高效方法

《在Golang中实现定时任务的几种高效方法》本文将详细介绍在Golang中实现定时任务的几种高效方法,包括time包中的Ticker和Timer、第三方库cron的使用,以及基于channel和go... 目录背景介绍目的和范围预期读者文档结构概述术语表核心概念与联系故事引入核心概念解释核心概念之间的关系

在Linux终端中统计非二进制文件行数的实现方法

《在Linux终端中统计非二进制文件行数的实现方法》在Linux系统中,有时需要统计非二进制文件(如CSV、TXT文件)的行数,而不希望手动打开文件进行查看,例如,在处理大型日志文件、数据文件时,了解... 目录在linux终端中统计非二进制文件的行数技术背景实现步骤1. 使用wc命令2. 使用grep命令

Python中Tensorflow无法调用GPU问题的解决方法

《Python中Tensorflow无法调用GPU问题的解决方法》文章详解如何解决TensorFlow在Windows无法识别GPU的问题,需降级至2.10版本,安装匹配CUDA11.2和cuDNN... 当用以下代码查看GPU数量时,gpuspython返回的是一个空列表,说明tensorflow没有找到

XML重复查询一条Sql语句的解决方法

《XML重复查询一条Sql语句的解决方法》文章分析了XML重复查询与日志失效问题,指出因DTO缺少@Data注解导致日志无法格式化、空指针风险及参数穿透,进而引发性能灾难,解决方案为在Controll... 目录一、核心问题:从SQL重复执行到日志失效二、根因剖析:DTO断裂引发的级联故障三、解决方案:修复