对象和数据结构之间有什么区别?

我一直在读《清洁代码:敏捷软件工艺手册》一书,在第六章第95-98页中,它阐明了对象和数据结构之间的差异:

  • 对象将其数据隐藏在抽象后面,并公开对该数据进行操作的函数。数据结构公开其数据,并且没有有意义的函数。

  • 对象公开行为并隐藏数据。这样,就可以在不更改现有行为的情况下轻松添加新类型的对象。它还使得向现有对象添加新行为变得困难。

  • 数据结构公开数据并且没有显著的行为。这使得向现有数据结构添加新行为变得容易,但使向现有函数添加新数据结构变得困难。

我有点困惑,有些类是对象还是数据结构。例如,在java.util中说HashMaps,它们是对象吗?(因为它的方法,如put(),get(),我们不知道它们的内部工作原理)还是它们是数据结构?(我一直认为它是数据结构,因为它是一个Map)。

字符串也是如此,它们是数据结构还是对象?

到目前为止,我编写的大部分代码都是所谓的“混合类”,它们也试图充当对象和数据结构。关于如何避免它们的任何提示?


答案 1

数据结构和类/对象之间的区别在Java中比在C++中更难解释。在C中,没有类,只有数据结构,它们只不过是类型化和命名字段的“容器”。C++继承了这些“结构”,因此您可以同时拥有“经典”数据结构和“真实对象”。

在 Java 中,您可以使用没有方法且只有公共字段的类来“模拟”C 样式的数据结构:

public class VehicleStruct
{
    public Engine engine;
    public Wheel[] wheels;
}

用户了解车辆的组成部件,并且可以直接与这些部件进行交互。行为,即函数,必须在类之外定义。这就是更改行为很容易的原因:添加新函数不需要更改现有代码。另一方面,更改数据需要更改与 交互的几乎每个函数。它违反了封装!VehicleStructVehicleStruct

OOP背后的想法是隐藏数据并公开行为。它侧重于您可以对车辆做些什么,而无需知道它是否有发动机或安装了多少个车轮:

public class Vehicle
{
    private Details hidden;

    public void startEngine() { ... }
    public void shiftInto(int gear) { ... }
    public void accelerate(double amount) { ... }
    public void brake(double amount) { ... }
}

注意,它可能是摩托车,汽车,卡车或坦克 - 你不需要知道细节。更改数据很容易 - 类外没有人知道数据,因此不需要更改类的用户。更改行为很困难:当向类中添加新的(抽象)函数时,必须调整所有子类。Vehicle

现在,按照“封装规则”,您可以将隐藏数据理解为简单地将字段设为私有并将访问器方法添加到:VehicleStruct

public class VehicleStruct
{
    private Engine engine;
    private Wheel[] wheels;

    public Engine getEngine() { return engine; }
    public Wheel[] getWheels() { return wheels; }
}

在他的书中,鲍勃叔叔认为,通过这样做,你仍然有一个数据结构,而不是一个对象。您仍然只是将车辆建模为其零件的总和,并使用方法公开这些零件。它本质上与具有公共字段和普通旧C的版本相同 - 因此是一个数据结构。隐藏数据和公开方法不足以创建对象,您必须考虑这些方法是否实际公开行为或仅公开数据!struct

当您混合使用这两种方法时,例如,与 一起公开时,您最终会得到一个“混合”。我手头没有马丁的书,但我记得他根本没有推荐混合体,因为你最终会得到两个世界中最糟糕的:数据和行为都难以改变的对象。getEngine()startEngine()

您关于HashMaps和Strings的问题有点棘手,因为它们非常低级,并且不太适合您将为应用程序编写的类。尽管如此,使用上面给出的定义,你应该能够回答它们。

A 是一个对象。它向您公开其行为并隐藏所有令人讨厌的哈希细节。你告诉它和数据,而不关心使用哪个哈希函数,有多少“桶”,以及如何处理冲突。实际上,您仅通过其界面使用,这很好地表明了抽象和“真实”对象。HashMapputgetHashMapMap

不要感到困惑,您可以使用Map的实例作为数据结构的替代品!

// A data structure
public class Point {
    public int x;
    public int y;
}

// A Map _instance_ used instead of a data structure!
Map<String, Integer> data = new HashMap<>();
data.put("x", 1);
data.put("y", 2);

另一方面,A几乎是一系列字符,并且不会试图隐藏太多。我想人们可以称之为数据结构,但说实话,我不确定是否能以这种或那种方式获得很多东西。String


答案 2

我相信,这就是罗伯特。C.马丁试图传达:

  1. 数据结构是仅充当结构化数据容器的类。例如:

    public class Point {
        public double x;
        public double y;
    }
    
  2. 另一方面,对象用于创建抽象。抽象被理解为:

    简化更复杂的事情,在“泄漏抽象法则”,Joel on Software

    因此,对象隐藏了它们的所有基础,只允许您以简化的方式操作其数据的本质。例如:

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

    我们不知道Point是如何实现的,但我们确实知道如何消费它。