zoukankan      html  css  js  c++  java
  • 01.考虑使用静态工厂方法替代构造方法

    前言

    《Effective Java》中文第三版,是一本关于Java基础的书,这本书不止一次有人推荐我看。其中包括我很喜欢的博客园博主五月的仓颉,他曾在自己的博文《给Java程序猿们推荐一些值得一看的好书》中也推荐过。加深自己的记忆,同时向优秀的人看齐,决定在看完每一章之后,都写一篇随笔。如果有写的不对的地方、表述的不清楚的地方、或者其他建议,希望您能够留言指正,谢谢。

    《Effective Java》中文第三版在线阅读链接:https://github.com/sjsdfg/effective-java-3rd-chinese/tree/master/docs/notes

    什么是构造方法?

    定义:

    • 一个在创建对象时自动被调用的方法。

    特点:

    • 构造方法的名称和类同名。
    • 没有返回值类型,即不能使用 return 关键字。
    • 普通方法不能以任何形式调用构造方法(构造方法中可以调用普通方法)。

    注意:

    • 当类中没有定义构造方法时,系统会默认添加一个无参的构造方法,当在类中定义构造方法的时候,默认的构造方法会消失。

    实例:

    public class Cat {
        //名字
        private String name;
    
        //颜色
        private String color;
    
        //年龄
        private Integer age;
    
        //构造方法的名称和类同名
        public Cat(){
    
        }
    
        //带一个参数的构造方法
        public Cat(String name){
            this.name = name;
            //普通方法不能以任何形式调用构造方法,构造方法中可以调用普通方法
            commonMethod();
        }
    
        //有两个参数的构造方法
        public Cat(String name, String color){
            this.name = name;
            this.color = color;
        }
    
        public void commonMethod(){
        }
    }

    什么是静态工厂方法?

    定义:

    • 在类中提供一个公共的静态方法,通过这个静态方法,对外提供自身实例。

    实例:

    public class Cat {
        //名字
        private String name;
    
        //颜色
        private String color;
    
        //年龄
        private Integer age;
        
        //创建只有名字的猫,带有一个构造参数
        public static Cat createCatWithName(String name){
            Cat cat = new Cat();
            cat.name = name;
            return cat;
        }
        
        //创建只有颜色的猫,带有一个构造参数
        public static Cat createCatWithColor(String color){
            Cat cat = new Cat();
            cat.color = color;
            return cat;
        }
        
        //创建有名字、颜色、年龄的猫
        public static Cat createCatWithAllParam(String name, String color, Integer age){
            Cat cat = new Cat();
            cat.age = age;
            cat.name = name;
            cat.color = color;
            return cat;
        }
    }

    为什么考虑使用静态工厂方法替代构造方法?(静态工厂方法的优点)

    • 与构造方法不同,静态工厂方法可以有多个入参数量相同,但名称不同的方法。

         实例:请参考上方的两个实例

    • 与构造方法不同,静态工厂方法都是有名字的。生成的代码更易于阅读,也能突出它们的差异。

         实例:

    public class Test02 {
    
        private String s1;
    
        private String s2;
    
        private String s3;
    
        private Integer i1;
    
        public Test02(){
        }
    
        public Test02(String s1){
            this.s1 = s1;
        }
    
        public Test02(String s1, String s2){
            this.s1 = s1;
            this.s2 = s2;
        }
    
        public Test02(String s1, Integer i1){
            this.s1 = s1;
            this.i1 = i1;
        }
    
        public Test02(String s1, String s2, String s3){
            this.s1 = s1;
            this.s2 = s2;
            this.s3 = s3;
        }
    
        public static Test02 createTest02InitAParam(String s1){
            Test02 result = new Test02();
            result.s1 = s1;
            return result;
        }
    
        public static Test02 createTest02InitTwoParam(String s1, String s2){
            Test02 result = new Test02();
            result.s1 = s1;
            result.s2 = s2;
            return result;
        }
    
        public static Test02 createTest02InitThreeParam(String s1, String s2, String s3){
            Test02 result = new Test02();
            result.s1 = s1;
            result.s2 = s2;
            result.s3 = s3;
            return result;
        }
    
        public static void main(String[] args) {
            //构造方法创建对象
            //前面我们说过,构造方法的名称和类同名,这导致构造函数的名称不够灵活,不能准确的描述返回值。
            //如果参数类型、数目比较相似的话,更加不容易找到合适的构造函数。例如test02与test04。
            Test02 test0 = new Test02();
            Test02 test01 = new Test02("A");
            Test02 test02 = new Test02("A","B");
            Test02 test03 = new Test02("A","B","C");
            Test02 test04 = new Test02("A", 1);
    
            //静态工厂方法创建对象
            //这让程序员更好的记得调用哪个方法创建适合自己需要的实例。注意仔细选择名称来突出它们的差异,这里我只是随便取的名字。
            Test02 aParam = Test02.createTest02InitAParam("A");
            Test02 twoParam = Test02.createTest02InitTwoParam("A", "B");
            Test02 threeParam = Test02.createTest02InitThreeParam("A", "B", "C");
        }
    
    }
    • 与构造方法不同,静态工厂方法不用每次调用时都创建一个新对象。使用预先构建的实例,反复分配它们避免了不必要的重复。这个特点在创建新对象的代价非常昂贵的情况下,是可以极大地提高性能的。

         实例:

    public static final Boolean TRUE = new Boolean(true);
    
    public static final Boolean FALSE = new Boolean(false);
    
    public static Boolean valueOf(boolean b) {
            return (b ? TRUE : FALSE);
        }
    • 与构造方法不同,静态工厂方法可以返回原返回类型的子类。例如一个应用是API,这让它可以返回对象而不需要公开它的类,这种方式隐藏类会使得API非常紧凑。

      实例:

    Class Person {
        public static Person getInstance(){
            return new Person();
            // return new Son01() / Son02()
        }
    }
    Class Son01 extends Person{
    }
    Class Son02 extends Person{
    }
    • 与构造方法不同,静态工厂方法可以入参值的范围。作为类的提供者,类入参的范围越大,调用者就越容易出错。下方实例中,调用者只用传入type即可调用。当提供者期望传入构造函数的值是事先定好的值时(下方TYPE_A、TYPE_B),但如果不是,就很容易导致程序错误。避免这种错误,我们可以使用静态工厂模式。调用方无须知道也无需制定type值,这样就能控制type赋值的范围。

      实例:

    public class Test03 {
        public static final int TYPE_A = 1;
        public static final int TYPE_B = 2;
        public static final int TYPE_C = 3;
        int type;
    
        private Test03(int type) {
            this.type = type;
        }
    
        public static Test03 newA() {
            return new Test03(TYPE_A);
        }
        public static Test03 newB() {
            return new Test03(TYPE_B);
        }
        public static Test03 newC() {
            return new Test03(TYPE_C);
        }
    
        public static void main(String[] args) {
            Test03 test03 = newA();
        }
    }

    静态工厂方法的缺点

    • 类如果不含有publicprotected的构造器,就不能被子类化。原因是父类缺少公有的构造方法,而子类无法调用父类的私有构造方法,导致子类无法生成构造方法。

         实例:

    public class Person {
        private String name;
        private Integer age;
    
        private Person(String name, Integer age){
            this.name = name;
            this.age = age;
        }
    
        public static Person getSon01(String name, Integer age){
            return new Person(name, age);
        }
    
        public static Person getSon02(String name, Integer age){
            return new Person(name, age);
        }
        
       //此时Student无法创建,因为Person没有公共的构造方法
        public class Son extends Person{
            
        }
    
    }

    总结


    静态工厂方法,语法层面

    • 能够有自己的名字
    • 用子类使代码更加紧凑。

    性能的层面

    • 避免创建大量等价的实例对象。

    最重要的是,当我们作为类的提供者,能够更好的控制调用者的具体行为,减少调用方出错的行为。我认为这也是对自己代码负责的体现。

  • 相关阅读:
    python解析网页
    node.js 爬虫
    c++ split实现
    foldl foldr
    爬虫http header gzip
    命令[10]
    命令[08]
    命令[15]
    命令[13]
    命令[11]
  • 原文地址:https://www.cnblogs.com/gongguowei01/p/12147738.html
Copyright © 2011-2022 走看看