第六章 对象和数据结构
- 变量设为私有(private)可以使外部不依赖于这些变量,并且可以变量的类型;
6.1 数据抽象
//此种数据结构清楚的表达了在笛卡尔坐标系下,可以单个操作,暴露了实现;即使变量私有,通过取值器和赋值器任然暴露了实现;
public class Point{
public double x;
public double y;
}
//此段掩盖了点是在极坐标还是笛卡尔坐标系下,但还是表现出了数据结构
public interface Point{
double getX();
double getY();
void setCartesian(double x, double y);
double getR();
double getTheta();
void setPolar(double r, double theta);
}
- 隐藏实现不是在变量上放一层函数那么简单,关键在于抽象
- 一个设计较好的类并不简单的使用取值器和赋值器将其变量暴露在外部,而是对外提供抽象的借口,使得使用者无需知晓类的内部细节
//这样的取值器直接暴露了类内部的实现
public interface Vehicle{
double getFuelTankCapacityCallons();
double getCallonsOfGasoline();
}
//隐藏了类中的细节,思想就是了解设计的目的(此处获取剩余油量),直接提供结果;胡乱添加取值器和赋值器是最为糟糕的结果
public interface Vehicle{
double getPercentFuelRemaining();
}
6.2 数据、对象的反对称性
- 对象把数据隐藏于抽象之后,暴露操作数据的函数
- 数据结构暴露其数据,而没有提供有意义的函数
//此种形式为数据结构形式,暴露数据,没有函数
public class Square{
public Point topLeft;
public double side;
}
public class Rectangle{
public Point topLeft;
public double height;
public double width;
}
public class Gemometry{
public final double PI = 3.141592653589793;
public double area(object shape) throws NoSunShapeException{
if(shape instanceof Square){
Square s = (Square)shape;
return s.side*s.side;
}
else if(shape instanceof Rectangle){
Rectangle r = (Rectangel) shape;
return r.hegiht*r.width;
}
throw new NoSuchShapeException();
}
}
//此种方式为对象方式,隐藏了实现,提供方法(未暴露实现)
public class Square implements Shape{
private Point topLeft;
private double side;
public double area(){
return side*side;
}
}
public class Rectangle implements Shape{
private Point topLeft;
private double height;
private double width;
public double area(){
return height*width;
}
}
总结:
数据结构的方法暴露了其本身的数据,但没有提供有意义的函数;面向过程
数据结构可以方便的添加函数,但添加数据结构类型十分不便;
原因在于添加一个数据结构类型需要修改所有已经存在的函数;
对象的方法保护了其私有数据,提供抽象的方法;面向对象
此方法可以方便的添加对象类型,应为每种类型是不相互干扰互相独立的;
但添加方法十分复杂,需要修改所有的对象类;
这两种方法是互补的,相反的;二分原理
在合适的地方使用合适的方法;
完全符合此思想的设计可能不容易存在,因此在设计时多加考虑;
6.3 德墨忒耳律(The Law of Demeter)
德墨忒耳律:模块不应了解它所操作对象的内部情形;
如上,对象隐藏数据,暴露操作,因此不应通过存储器爆露其内部结构;
德墨忒耳律认为,类C的方法f只应该调用以下对象的方法:
- C;
- 由f创建的对象;
- 作为参数传递给f的对象;
- 由C的实体变量持有的对象;
6.3.1 火车失事
final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();
- 这样的代码被称为火车失事,代码风格十分不友好,应做切分;
- 数据结构只简单地拥有公共变量,而没有函数;对象则拥有私有变量和公共函数;
6.3.2 混杂
- 有些框架会要求数据结构拥有改值器和取值器,使得代码混乱,导致一半是对象,一半是数据结构;
- 这样的混杂加大了添加新函数的难度,也添加了增加新类型的难度;
6.3.3 隐藏结构
思考设计意图,主动完成,从而隐藏结构;
//代码意图是获得地址创建文件,下面这两种方法或多或少暴露了数据结构; ctxt.getAbsolutePathOfScratchDirectoryOption(); ctxt.getScratchDerictoryOption().getAbsolutePath(); //通过设计意图进行代码重构,隐藏对象结构; BufferedOutputStream bos = ctxt.createScratchFileStream(classFileStream);
6.4 数据传输对象
- 数据结构是只有公共变量,没有函数的类;这种数据结构被称为数据传送对象;