Effective Java其他碎片建议2

来源:互联网 发布:手机能申请淘宝店铺吗 编辑:程序博客网 时间:2024/05/22 12:36

Rule 15、使可变性最小化

为了使类成为不可变,遵循以下五条规则:

1、不要提供任何会改变对象状态的方法(也称为mutator)

2、保证类不会被扩展(一般是使用final关键字修饰类 但是也有其他的方法)

3、使所有域都是final的

4、使所有域都是私有的

5、确保对于任何可变组件的互斥访问(保证类的客户端无法获取指向这些对象的引用(使用保护性拷贝))

不可变对象本质上是线程安全的,他们不要求同步。

不可变对象真正唯一的缺点是,对于每个不同的值都需要一个单独的对象。



Rule 16、复合优先于继承

继承是实现代码重用的有力手段,但不一定是最好的。

继承打破了子类的封装性。当子类真正是超类的子类型(subtype)时,才适合用继承(is-a)。

有时候,B应该包含A一个私有实例,并且暴露出一个简单的、较小的API,A本质上不是B的一部分,这就不该用继承,而是复合。

然而sun公司的大师们也会犯这样的错误,比如stack不是vector,所以Stack不应该扩展VectorProperties也不是Hashtable,Properties就不应该扩展Hashtable,这样情况下,复合要好一点。

如果在应该使用复合的地方使用了继承,则会不必要地暴露实现细节。客户端就有可能直接访问这些内部细节。比如p指向了Properties实例,p.getProperties(key)就可能产生和p.get(key)不同的结果。



Rule 17、要么为继承设计并且提供文档说明,要么就禁止继承

如题,文档应该精确地描述覆盖每个方法带来的影响,换句话说,该类必须有文档说明它可覆盖的方法的自用性。



Rule 18、接口优先于抽象类

首先看看接口和抽象类的区别:抽象类允许包含某些方法的实现,但是接口则不允许。更重要的一点在于,如果想实现抽象类定义的类型,类必须成为抽象类的一个子类,然而java是单继承的。

现有的类可以很容易被更新,以实现新的接口;

接口是定义混合类型(mixin)的理想选择;

接口允许我们构造非层级结构的类型框架,而不是使用继承抽象类的臃肿的类层次;



Rule 19、接口只用于定义类型

当类实现接口时,接口就充当可以应用这个类的实例的类型。因此类实现了接口就表明客户端可以对这个类的实例实施某些操作,而出于其他任何目的定义接口都是在耍流氓。

有种接口叫做常量接口(constant interface),他没有任何的方法,只包含静态final域,每个域导出一个常量:

public interface Boy{    static final int AGE = 19;    static final String LOCATION = "DLUT";}
这是对接口的不良使用。类实现常量接口,这完全是对接口的滥用,会导致接口中的实现细节透露到导出API中,类实现常量接口对这个类的用户来讲没什么价值,而且容易给用户造成困惑。

如果就是想要实现类似的功能,可以利用静态导入(static import)机制:

import static 包名.*;//导入包名下的常量public class Test{    //访问}
简而言之,接口应该只被用来定义类型。



Rule 20、类层次优于标签类

有时候,可能会遇到带有两种甚至更多风格的实例的类,并包含表示实例风格的标签(tag)域,比如下面的代码:

class Figure{    enum Shape{RECTANGLE,CIRCLE};    //tag field - the shape of this figure    final Shape shape;    //These fields are used only if shape is RECTANGLE    double length;    double width;    //this field is used only if shape is CIRCLE    double radius;    //Constructor for circle    Figure(double radius){        shape = Shape.CIRCLE;        this.radius = radius;    }    //Constructor for rectangle    Figure(double length,double width){        shape = Shape.RECTANGLE;        this.length = length;        this.width = width;    }    double area(){        switch(shape){            case RECTANGLE:                return length * width;            case CIRCLE:                return Math.PI * (radius * radius);            default:                throw new AssertionError();        }    }}

他们充斥着样板代码,包括枚举类型、标签域以及条件语句。可读性很糟糕,还增加了内存占用。总之,标签类冗长、容易出错、效率低下。我们可以使用类层次来解决这个问题,比如使用一个形状类Figure,使用Circle、Rectangle分别继承这个形状类Figure,可以更清楚地解决这个问题。




未完待续...