代码整洁之道(五)---------------------数据结构与对象

来源:互联网 发布:更改位置定位软件 编辑:程序博客网 时间:2024/05/08 00:05

         关于代码整洁方面,还想说的就是数据结构与对象,很多程序员对其概念不明,写代码的时候就颇为混乱。有时候将变量设置为私有(private,是不想将对象的变量公之于众,可是很多人又会自动的给该对象添加赋值器和取值器,那该私有变量与公共变量又有和区别?

想要弄明白对象与数据结构的区别,以及如何在写代码的时候正确运用,就先得弄明白数据抽象的概念。

(1)数据抽象

我们来先看两段代码,

代码一:

public class Point {
    public double x;
    public double y;

 

代码二:

public interface Point{
    double getX();
    double getY();
    void setCartesiain(double x,double y);
    double getR();
    double getTheta();
    void setPolar();
}

 

代码二的高明之处在于,你不知道该实现会是在一个矩形坐标系还是极坐标系中,可能两个都不是,它不仅明白无误的呈现出一种数据结构,那些方法固定了一套存取策略,可以单独读取某一坐标,但必须通过原子操作(指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束)赋值。

而代码一,你可以很明白他是在矩形坐标系中去实现,并要求我们单独操作那一个坐标,这就曝露实现,实际上,即便变量私有,我们用取值器与赋值器,仍然曝露了实现。

曝露实现并非只是在变量之间放上一个函数层那么简单,隐藏实现关乎抽象,类并不是简单的用取值器与赋值器将其变量向外推,而是曝露抽象接口,这样使用者无需了解数据的实现就可以操作数据的本体。

看看下面两段代码,想要知道现在定时器完成任务的状态,代码三通过具象的手段获得定时器的当前参数,而后者通过百分比抽象。

代码三:

public interface MyTimer{
    double getStartTime();
    double getEndTime();
    double getCurrentTime()

}

代码四:

public interface MyTimer{
    double getPercentOfCompletedTime();
}

如果我们不愿暴露数据细节,更愿意以抽象的形态表述数据。这不是简单的用接口和赋值器与取值器就万事大吉了,要如何呈现某个对象包含的数据,需要认真地思考,不要傻乐的添加赋值器与取值器,那是最坏的选择。

(2)数据、对象的互补

  下面两个例子展示了对象与数据结构的差异。对象把数据隐藏在数据抽象之后,曝露数据操作的函数,数据结构曝露其数据,没有提供有意义的具体操作函数。他们是对立的这种差异貌似微小,但却有深远影响。

代码五:

class Square {
     public double width;
    public double height;
}

class Circle{
    public double radius;
}

class Geometry{
    public final double PI 3.141592653589793;
    public double area(Object shape) throws NoClassDefFoundError{ 
        if(shape instanceof Square){
           
            return ((Square) shape).height*((Square) shape).width
        }else if(shape instanceof Circle){
            return ((Circle) shape).radius*((Circle) shape).radius*PI;
        }
        throw new NoClassDefFoundError();
    }
}

 

 代码六:


interface Shape {
    double area();
}
class Square implements Shape {
    private double width;
    private double height;

    @Override
    public double area() {
        return width*height;
    }
}

class Circle implements Shape{
    private final double PI 3.141592653589793;
    private double radius;

    @Override
    public double area() {
        return radius*radius*PI;
    }
}

对于代码五。有人可能嘲笑它太过程化,而代码六是标准的面向对象的方案,area()是多态的,不需要Geometry类。所以再添加一个形状,现有函数一个都不会受影响,而当添加新函数时所有的形状都得改。二代码五去刚好相反,假如我想再添加一个去周长的函数,现有的形状类不会受到影响,但是添加一个新的形状就的修改方法area()。

   我们可以看到这两种定义如此对立,这也说明了对象与数据结构的二分原理。所以面向对象难以做到的,面向过程式的代码就相对容易。

3)混杂

   著名的德墨忒尔定律(The Law Of Demeter)认为,模块不应该了解他所操作的对象的内部情况。也就是说,对象隐藏数据,曝露操作,尽量避免通过存储器曝露其内部结构。   

有的时候我们会看到类似这样的代码,他就违反了德墨忒尔定律,

final String current_manager_name = MyApplication.getInstance().getCurrent_manger().getName();

 

这类代码常被称为火车失事。我们应该避免这样的代码。他混淆了数据结构与对象。

你要么把它改成:

final  String current_manager_name =  MyApplication.Current_manger.Name;

 

或者:

final String current_manager_name new MyApplication.getCurrent_manger_Name();

 

有时候我们会用“豆”(bean)结构,注意,他算不上对象,只是一种数据结构。他的这种半封装形式只是一些纯面向对象者感觉舒服的一种假像。但不幸运的是,很多研发者在这类数据结构里塞很多业务规则方法,把这类数据结构当对象来用。这种混杂不属于整洁代码的范畴,解决方案就是在这种数据结构里添加一个对象,该对象包含处理业务逻辑的函数。

 

最后总结一下,对象曝露行为,隐藏数据。便于添加新的对象类型而无需修改既有的行为,同时也难以添加新的行为。数据结构曝露数据,没有明显的行为。便于向既有的数据结构添加新的行为,同时也难以添加新的数据结构

0 0
原创粉丝点击