可以工作的类

可以工作的类

类的基础

抽象数据类型(ADT,abstract data type)是指一些数据以及操作这些数据的集合。抽象数据类型可以让你像实现世界一样操作实体,而不必在底层的实现上摆弄实体。不要让抽象数据类型依赖于存储介质。


ADT和类
抽象数据类型构成了“类/class”这一概念的基础。可以把类看成是抽象数据类型加上继承和多态的两个概念。

良好的接口
创建一个可以通过接口来展现的抽象,并确保细节仍被隐藏在抽象背后。类的接口:基于类所具有的公用(public)子程序所构成的集合。创建类的抽象接口的指导建议

1,类的接口应展现一致的抽象层次;

在考虑类的时候有一种很好的方法,就是把类看成一种用来实现抽象数据类型的机制。 每个类应该现实一个ADT,并且仅现实这个ADT。如果你发现某个类现实了不止一个类ADT,或者你不能确定究竟它实现了何种ADT,你就应该把这个类重新组织为一个或多个定义明确的ADT。在下面的例子中,类的接口不够协调,因为它的抽象层次不一致:

import java.util.ArrayList;

// 雇员类
class Employee{
}

// 混合了不同层次抽象的类接口
public class EmployeeCensus<Employee> extends ArrayList<Employee> {
    private static final long serialVersionUID = 5475849176649878778L;
    
    // 下面的两个方法的抽象在“雇员”这一层次上
    public void addEmployee(Employee employee){...}
    public void removeEmployee(Employee employee){...}
    
    
    // 下面这些方法的抽象在“列表”这一层次上
    public Employee nextItemInList(){...}
    public Employee firstItem(){...}
    public Employee lastItem(){...}
}
这个类展现了两个ADT:Employee和ArrayList。出现这种混合的抽象,通常是源于程序员使用容器类或其它类库来实现内部逻辑,但却没有把“使用类库”这一事实隐藏起来。如下面修改后的类接口:
import java.util.ArrayList;
import java.util.List;

// 雇员类
class Employee{
}

// 有着一致层次抽象的类接口
public class EmployeeCensus{
    private static final long serialVersionUID = 5475849176649878778L;
   
    // 隐藏实现细节
    private List<Employee> employeeList = new ArrayList<Employee>();
    
    // 所有这些子程序的抽象现在都是在“雇员”这一层次上了
    public void addEmployee(Employee employee){...}
    public void removeEmployee(Employee employee){...}
    public Employee nextEmployee(){...}
    public Employee firstEmployee(){...}
    public Employee lastEmpolyee(){...}
}

2,一定要理解类所实现的抽象是什么;
3,考虑是否需要提供成对的服务;
如果有一个操作把灯打开,那么很可能也需要另一个操作来把灯关闭。
4,把不相关信息移到其他类中;
5,尽可能让接口编程,而不是表达语义;
每个接口都由一个可编程的部分和一个语义部分组成。可编程的部分由接口中数据类型及其他属性构成,编译器能强制性要求它们(在编译时检查错误)。而语义部分则由“本接口将会被怎样使用”的假定组成,而这些是无法通过编译器来强制实施的。接口语义应通过注释说明,但要尽可能让接口不依赖于这些说明。
6,谨防在修改时破坏接口的抽象;
7,不要添加与接口抽象不一致的公用成员;
8,同时考虑抽象性和内聚性;

良好的封装
抽象通过提供一个可以让你忽略现实细节的模型来管理复杂度,而封装则强制你看到细节---即使你想这么做。这两个概念之所以相关,是因为没有封装时,抽象往往很容易打破。
1,尽可能的限制类和和成员的可访问性;
2,不要公开暴露成员数据;
3,避免把私有的实现细节放入类的接口中;
4,不要对类的使用者做出任何假设;
它不应该对接口如何使用或被如何使用做出任何假设---除非在接口中有过明确说明;
5,不要因为一个子程序里使用公用子程序,就把它归入公开接口;
一个子程序仅仅使用公用的子程序这一事实并不是十分重要的考虑要素。相反,应该问的问题是,把这个子程序暴露给外界后,接口所展示的抽象是否还是一致的。6,让阅读代码把写代码更方便;
7,要格外警惕从语义上破坏封装性;
8,留意过于紧密的耦合关系,指两个类之间关联的紧密程序;

关于设计和现实的问题
组合

它表示一个类含有一个基本数据类型或对象
1,通过组合现实“有一个/has a”的关系;
2,在万不得已时通过private继承“有一个”的关系;
3,警惕有超过7个数据成员的类,研究表明人在做其他事情能记住的离散项目是7+2个或7-2个;

继承
继承的概念是说是一个类是另一个类的特化。继承的目的在于“定义能为两个或更多个派生类提供共有元素的基类”的方式写出更精简的代码。
当决定使用继承时,你必须做如下几项决策:
1,对于每一个成员函数而言,它应该对派生类可见吗?你应该有默认的现实吗?这个默认的现实能被覆盖吗?
2,对于每一个数据成员而言(变量、具名常量、枚举),它应该对派生类可见吗?
下面详细解释如何考虑这些事项
1,实现“是一个...”的关系;
2,要么使用继承并进行详细说明,要么就不使用它;
3,遵循Liskov替换原则,即可总结为”派生类必须能通过基类的接口而被使用,且使用者无须了解两者之间的差异。“;
4,确保只继承需要继承的部分;
5,把公共的接口、数据及操作放到继承树中尽可能高的位置;
6,只有一个类的实例是值得怀疑的;
7,只有一个派生类的基类也是值得怀疑的;
8,派生后继承了某个子程序,但在其中没有任何操作,这种情况也值怀疑;
9,避免让继承体系过深;
10,尽量使用多态,避免大量的类型检查;
11,让所有的数据都是private;

成员函数和数据成员
一般来说,尽量减少类和类之间相互合并的范围,尽量让下面这几个数字最小
1,所实例化的对象的种类;
2,在被实例化对象上直接调用的不同子程序的数量;
3,调用由其它对象返回的对象的子程序的数量;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值