第四章: 类和接口
引言
这章主要讲了类和接口的设计原则,如何设计除更健壮,更灵活的类和接口.
正文
使类和成员的访问最小化以及在公有类中使用访问方法而非公有域
前面两条实质上讲的是一件事,就是不要暴露类的内部成员,如果是公有类(一般情况我们写的都是public
的类)一定不能暴露内部的成员,而是使用getter
和setter
.
尽可能的降低内部成员的可访问性. 这么做的好处就是封装
性比较强, 也灵活很多,如果将来想在类的成员上做限制,直接在getter
或者setter
上做改变就可以, 外部调用者完全感受不到变化.
如果编写一个不可变的类
- 不要提供任何会修改对象状态的方法–比如
setter
- 保证类不会被扩展–使用
final
修饰符或者private
的构造器. - 将所有的域(类的成员)都设置成
private final
的. - 确保任何可变组件的互斥访问–就是如果类的成员是一个对象引用, 则保证这个引用不会被外部获取到.
functional
: 不改变当前实例, 而是创建一个返回一个新的实例的方法. 这种方法被称为functional
. 这种方法的方法名都使用介词.
不可变对象的优点:
- 不可变对象是线程安全的, 不要求同步.
- 不可变对象可以自由的共享, 甚至可以共享他们的内部信息.
- 不可变对象可以为其他对象提供构件.
- 不可变对象提供了原子性.
不可变对象的缺点
每一个不同的值都需要一个对象, 如果创建不可变对象代价太高, 那么可能会导致程序性能下降. 一般不可变对象都提供一个对应的可变的配套类,比如String
和StringBuilder
.
组合(复合)优于继承
- 继承打破了封装性.子类依赖父类其中特定的功能细节,如果将来父类改变了,那么子类也会跟着发生相应的变化,往往这些变化不是子类自己能控制的. 除非父类是专门用来被继承的或者有很好的说明该如何继承该类, 否则最好使用组合.
- 如果A和B两者直接存在
is-a
的关系才应该使用继承, 在实现继承时候, 要反复的确定 B 在任何时候都是一个 A 类型. 如果不是, B 仅仅是使用了 A 的一个功能, 那么就应该使用组合模式.
要么设计继承并提供文档, 要么禁止继承
这条其实是对于上面那条的补充说明, 上一条说如果继承了一个不是用来被继承的类,是一件很危险的事情, 可能导致封装性被破坏, 在设计一个被继承的类时, 要有良好的说明
- 该类必须有说明可以被重写的方法的
自用性
–即类必须在文档中说明,在哪些情况下它会调用被重写的方法. - 对于为了继承而设计的类, 必须在发布之前先编写子类对其进行测试.
- 构造器不可以调用可以被重写的方法.
clone
和readObject
方法也不能调用可以被重写方法.
接口优于抽象类
如果是对实现类的规范和约束则应该使用抽象类, 如果想实现多继承则应该使用接口. 接口的可扩展性要优于继承.
接口的使用
- 为后代设计接口.
- 接口只用于定义类型.
- 不要使用常量接口.
写在最后
整章对于在写代码设计类的层次接口非常有用, 但是由于使用spring
框架, 很多事情spring
都帮你做了, 这些设计原则用到的机会不是非常多, 导致里面有很多东西看了似懂非懂, 所以以后还是要离开spring
框架单独做一些非web
项目, 这样才能有较深的感悟.