- 使类和成员的可访问性最小化
一个模块设计良好与否,最重要的因素在于,这个模块对于外部的其他模块而言是否隐藏了内部数据和其他实现细节,模块之间仅仅通过API通信,这是软件设计的基础,因此,除了公有静态final域的特殊情形之外,公有类都不应该包含公有域,并且要确保公有静态final域不包含可变对象的引用。
- 在公有类中使用访问方法而非公有域
公有类永远不应该暴露可变的域,这点没什么好解释的,破坏了模块化的基本规则,还会带来各种性能问题。
- 使可变性最小化
不可变类指的是类的实例不可改变的,每个实例中包含的所有信息都应该在创建的时候就初始化,并在整个生命周期中不再改变。要实现一个不可变类,应该遵循五个原则:
- 不提供任何改变对象状态的方法。
- 保证类不会被扩展,比如使用final修饰类。
- 使所有的域都是final的
- 使所有的域都成为私有
- 确保对于任何可变组件的互斥访问
- 复合优先于继承
继承非常强大,但因为它破坏了封装性,因此也会带来许多问题,只有当类之间确实存在is-a关系时,使用继承才是恰当的,如果超类不是专门为继承而设计的,那么继承将导致脆弱性,继承将把所有设计缺陷全部传递给子类,而复合允许设计新的API来隐藏这些缺陷。
- 要么就为继承而设计,并提供文档说明,要么就禁止继承
编写超类时,类文档必须明确说明覆盖每个方法所带来的影响,必须在共有或者受保护的方法和构造器中,说明调用了哪些可覆盖方法,在什么情况下调用。对于为了继承而设计的类,唯一而且必要的测试方法是编写子类测试,通常编写三个子类就足以测试一个可扩展的类。为了能够继承,类还必须遵守一些其他约定,在超类中,构造器决不能调用可被覆盖的方法,因为父类构造器在子类之前执行,如果所覆盖的方法依赖于子类构造器执行的初始化工作,那么就会导致错误。如下所示:
pubilc class Super{ public Super(){ overrideMe(); } public void overridMe(){ } } public final class Sub extends Super{ private final Date date; Sub(){ date = new Date(); }; @Override public void overridMe(){ //这里将会输出两次,但是第一次是NULL System.out.print(date); } }
- 接口优先于抽象类
这样做的好处有如下几个:
- 现有的类很容易被更新,以实现新的接口
- 接口是定义混合类型的理想选择,而抽象类由于单继承的原因无法实现这一点
- 接口允许构造非层次关系的类型框架
需要注意的是,设计公有接口必须谨慎,一旦公开并且广泛实现,想要改变这个接口就几乎不可能。接口是定义允许多个实现的类型的最佳途径,只有一个例外,就是当演变的容易性比灵活性和功能性更重要的时候,应该使用抽象类来定义类型,在抽象类增加一个新方法,所有抽象类都将实现这个方法,接口做不到这一点。
- 接口只定义类型
当类实现接口时,接口就充当可以引用这个类的实例的“类型”,定义一个接口的目的应该只是为了对它的实现类实施某种操作,其中,常量接口例外,这种接口只包含静态final域,不包括任何方法,因此也没有什么实例化的价值,还把模块的实现细节暴露了,所以也不建议用接口来导出常量。
- 类层次优先于标签类
模块化,高内聚,低耦合正是面向对象的思想,因此多个不同的事物应该抽象化,进行模块化封装,通过继承、组合等方式关联,形成层次分明的类结构,而标签类与之相反,混乱,充斥着各种标签域和条件声明,影响可读性并且容易出错,应该避免使用。
- 用函数对象表示策略
如果一个类仅仅导出这样一个方法,它的实例实际上等同于一个指向该方法的指针。这样的实例被称为函数对象。如下所示:
//一个函数对象 class StringLengthComparator{ public int compare(String s1,String s2){ return s1.length() - s2.length(); } }
函数对象的主要用途是实现策略模式。
- 优先考虑使用静态成员
嵌套类指的是被定义在一个类内部的类,存在的目的就是为外围类提供服务。嵌套类有四种:静态成员类、非静态成员类、匿名类、局部类。后面三种都叫内部类。静态成员类和非静态成员类的区别如下:
(1)内部静态类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。
(2)非静态内部类能够访问外部类的静态和非静态成员。静态类不能访问外部类的非静态成员。他只能访问外部类的静态成员。
(3)一个非静态内部类不能脱离外部类实体被创建,一个非静态内部类可以访问外部类的数据和方法,因为他就在外部类里面。
如果成员类不要求访问外围实例,就要始终把static修饰符放到声明中,使它成为静态类而不是非静态类。每个非静态类都包含一个指向外围对象的引用,这非常消耗时间和空间,并且在外围实例垃圾回收时仍会保留。如果只需要在一个地方创建实例,并且有了预制类型表明这个类的特征,就应该声明为匿名类,否则就是局部类。