Setter/Getter之争,有点意思

1,654 阅读3分钟

这几天在做 Code Review 的时候,发现 Model 层内大量 Bean 写法不一。

有些类是属性开放一路 public,有些类则属性全部封闭 private。大家针对这个问题讨论得还挺激动,着实出乎意料。

听他们的说辞,观点都挺有道理的。

封闭派支持者持以下观点。

Java 官方推荐针对实体类 Bean 进行封装,确保对外层调用者隐藏敏感数据。应该将类变量或属性声明为私有并提供公共获取及设置的方法,以便访问和更新私有变量的值。

的确。倘若一个属性是以 obj.field 的方式访问或直接赋值更新,当 field 的访问及更新逻辑需要调整时则得重新编写方法支持。

举个🌰。一个包含直接访问及更新变量的类如下。

Class Obj{
  public int value;
}
Obj obj = new Obj();

//使用属性直接访问或更新
obj.value = -1;
int objValue = object.value;

而采用封闭开放原则编写的类为:

Class Obj{
  private int value;
  
  public void setValue(int value){
    this.value = value;
  }
  
  public int getValue(){
    return value;
  }
}
Obj obj = new Obj();

//使用方法调用的方式访问或更新
obj.setValue(-1);
int objValue = object.getValue();

Obj 希望限定 value 的值在大于等于 0 的值区间时需调整 Obj 类的实现。

Class Obj{
  private int value;
  
  //限定逻辑
  public void setValue(int value){
    if(value < 0){
      value = 0;
    }
    this.value = value;
  }
  
  public int getValue(){
    return this.value;
  }
}

这样看起来,封闭派的写法确有前瞻性的。在未来,无论属性的访问及更新值逻辑要做怎样的调整,对于外部调用者而言是完全透明的。

开放派支持者其实并不反对封闭派的说辞,但针对封闭派所有 Bean 实体都写成这种格式,且看提交记录这些类大部分都已经两三年未修改的现状颇为不满。

有些用于描述 Android 设备信息,网络信息,App信息等固定内容的类,其属性并不会轻易进行修改。

想想也是,如AppInfo类用于描述 App 信息。

public class AppInfo{
  public String appId;
  public String versionName;
  public String verionCode;
  public String platform;
  public String channel;
  public String osVersion;
  public String sdkVersion;
  public String deviceId;
  public String packageName;
}

在没有 JIT 场景下,直接字段访问要比调用 getter 方法快大约 3 倍;在有 JIT 场景下,直接字段访问要比方法调用快大约 7 倍。

当然。开放派支持者也会考虑该类的设计是否合理。上述变量 appId,希望在测试环境下追加一个 test 的后缀标识。比如正式环境下 appId 值为 com.android.demo,测试环境则为 com.android.demo.test。可调整实现为:

public class AppInfo{

  public String appId;
  
  //新增方法用于返回兼容后的 appId
  public String getCompatAppId(){
    if(BuildConfig.TEST){
       return appId + ".test";
    }
    return appId;
  }
}

又或者调整为:

public class AppInfoUtils{

   //使用工具类对 appId 做二次转化
   public getCompatAppId(String appId){
      if(BuildConfig.TEST){
         return appId + ".test";
      }
      return appId;
   }
}

这两种方法都可。

开放派认为

  1. appId 属性不应该被轻易改动,没有必要过渡设计;
  2. 哪怕真的需要改动,改动也没有破坏属性的定义,可通过新增功能函数来解决这个问题即可。

一百个编程者心里有一百个哈姆雷特啊!

聊一聊我的看法。

在实际项目我确实也不会为所有 Bean 类编写 setter/getter 方法,因为会让代码变得冗余且调用相对麻烦。但在封装设计SDK上,考虑到 SDK 维护及 API 更改的成本,还是遵循了开放封闭的设计原则,谨慎为好。

但代码看起来就是别扭呀。

好在Koltin解决了我内心的纠结。

对于 var 关键字申明的属性,gettter/setter是可选的,得益于幕后字段的设计,属性的访问及更新变为可干预的。

最后分享下最近有感的一句话

Fucking code, just happy!

欢迎关注 「Android之禅」公众号,和你分享有价值有思考的技术文章。
可添加微信 「Ming_Lyan」备注 “进群” 加入技术交流群,讨论技术问题严禁一切广告灌水。
如有 Android 领域有遇到技术难题亦或对未来职业规划有疑惑,一起讨论交流。
欢迎来扰。