关于java:对象和数据结构之间的区别是什么?

Whats the difference between objects and data structures?

我一直在读《清洁代码:敏捷软件技术手册》,在第六章第95-98页中,它澄清了对象和数据结构之间的区别:

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

  • 对象公开行为并隐藏数据。这使得在不改变现有行为的情况下添加新类型的对象变得容易。这也使得向现有对象添加新行为变得困难。

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

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

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

到目前为止,我编写的大多数代码都是所谓的"混合类",它们试图充当对象和数据结构。还有什么关于如何避免它们的建议吗?


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

在爪哇中,可以使用没有方法和只有公共字段的类"仿真"C风格的数据结构:

1
2
3
4
5
public class VehicleStruct
{
    public Engine engine;
    public Wheel[] wheels;
}

VehicleStruct的用户知道车辆的部件,可以直接与这些部件交互。行为,即函数,必须在类之外定义。这就是为什么很容易更改行为:添加新函数不需要更改现有代码。另一方面,更改数据实际上需要更改与VehicleStruct交互的每个函数。它违反了封装!

OOP背后的想法是隐藏数据并公开行为。它关注的是在不必知道是否有发动机或安装了多少车轮的情况下,您可以对车辆执行什么操作:

1
2
3
4
5
6
7
8
9
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添加访问方法:

1
2
3
4
5
6
7
8
public class VehicleStruct
{
    private Engine engine;
    private Wheel[] wheels;

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

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

当你把这两种方法混合在一起时,例如将getEngine()startEngine()一起暴露,你最终会得到一个"混合体"。我手头没有马丁的书,但我记得他根本不推荐混合动力车,因为你最终会遇到两个世界中最糟糕的:数据和行为都难以改变的对象。

关于散列图和字符串的问题有点棘手,因为这些问题的级别非常低,不适合您为应用程序编写的类。然而,使用上面给出的定义,您应该能够回答它们。

HashMap是一个对象。它向您公开它的行为,并隐藏所有令人讨厌的散列细节。你把它告诉putget数据,不关心使用的是哪个散列函数,有多少"bucket",以及如何处理冲突。实际上,您仅仅通过它的Map接口使用HashMap,这是抽象和"真实"对象的一个很好的指示。

不要对可以使用映射实例替换数据结构感到困惑!

1
2
3
4
5
6
7
8
9
10
// 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);

另一方面,String几乎是一个字符数组,并且不会试图隐藏太多。我想有人可以称之为数据结构,但老实说,我不确定是否可以通过这种或那种方式获得很多。


对象是类的实例。一个班可以从现实世界中模拟各种事物。这是一个抽象的东西(汽车,插座,地图,连接,学生,老师,你命名它)。

数据结构是以某种方式组织某些数据的结构。您可以使用不同于使用类的方式实现结构(这是您在不支持OOP的语言中所做的工作,例如,您仍然可以使用C语言实现数据结构)。

Java中的HashMap是一个使用基于哈希的实现对地图数据结构进行建模的类,这就是为什么它被称为Hash映射。

Java中的Socket是一个类,它不建模数据结构,而是其他的(套接字)模型。


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

  • 数据结构是简单地充当结构化数据容器的类。例如:

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


    a simplification of something much more complicated that is going on under the covers The Law of Leaky Abstractions, Joel on Software

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

    1
    2
    3
    4
    5
    6
    7
    8
    public interface Point {
        double getX();
        double getY();
        void setCartesian(double x, double y);
        double getR();
        double getTheta();
        void setPolar(double r, double theta);
    }

    我们不知道该点是如何实现的,但我们知道如何使用它。


  • 正如我所看到的,罗伯特·马丁试图传达的是,对象不应该通过getter和setter公开数据,除非它们的唯一目的是充当简单的数据容器。这样的容器的好例子可能是Java bean、实体对象(来自DB实体的对象映射)等。

    然而,Java集合框架类并不是他所指的一个很好的例子,因为它们并没有真正公开其内部数据(这在很多情况下是基本数组)。它提供了抽象,让您检索它们包含的对象。因此(在我的POV中)它们适合"对象"类别。

    原因是由你从书中添加的引语来说明的,但是还有更多的好理由避免暴露内部。例如,提供getter和setter的类会违反demeter的法律。此外,了解某个类的状态结构(知道它拥有哪些getter/setter)会降低抽象该类实现的能力。还有很多这样的原因。


    数据结构只是一种抽象,一种表示数据的特殊方式。它们只是人为构造,有助于降低高层的复杂性,即不在底层工作。一个对象可能意味着相同的事情,但是对象和数据结构之间的主要区别是对象可能抽象任何东西。它还提供行为。数据结构没有任何行为,因为它只是保存内存的数据。

    库类(如map、list等)是表示数据结构的类。它们实现并设置数据结构,以便通过创建它们的实例(即对象)在程序中轻松地使用它们。


    数据结构实际上是一些数据的结构化表示。除了数据的结构和命名之外,它没有任何内置的"知识"。对象更为一般,实际上可以是系统中几乎任何一个为交互提供接口的实体,并且可以在不同的上下文中使用。具体来说,例如,传统的类对象可能包含数据和访问数据的合适方法,并且该数据可能是特定于对象实例(每个实例)或类级别的。

    我不确定术语"混合类"是基于作为对象或数据结构的描述,因为类通常包含数据。所以这个术语似乎是多余的。


    数据结构(DS)是一种抽象的表示结构拥有某些数据的方式。具有一些关键值对的HASMAP是Java中的数据结构。相关联的数组在php等中类似。对象比ds级别低一点。您的哈希图是一个数据结构。现在要使用哈希映射,您需要创建一个"对象",并使用Put方法向该对象添加数据。我可以有我自己的班级员工,他们有数据,因此是我的DS。但是要使用这个ds来做一些操作,比如查看雇员是男性还是女性同事,我需要一个雇员的实例并测试其性别属性。

    不要将对象与数据结构混淆。


    对象是类的实例。类可以定义该类的每个实例/对象继承的一组属性/字段。数据结构是组织和存储数据的一种方法。从技术上讲,数据结构是一个对象,但它是一个专门用来保存其他对象的对象(Java中的所有东西都是对象,甚至是原始类型)。

    要回答您的问题,字符串是一个对象和数据结构。您创建的每个字符串对象都是string类的实例。一个字符串,如Java在内部表示的,本质上是一个字符数组,而数组是一个数据结构。

    并非所有的类都是数据结构的蓝图,但是如果有任何意义的话,所有的数据结构在技术上都是对象,即类的实例(专门设计用于存储数据)。


    您的问题被标记为Java,所以这里只引用Java。对象是Java中的EVE类;也就是说,Java中的所有对象都扩展对象和对象。

    因此,所有的数据结构都是对象,但并非所有的对象都是数据结构。

    区别的关键是术语封装。

    在Java中创建对象时,最好将所有数据成员私有化。你这样做是为了保护他们不被任何人利用。

    但是,您希望人们能够访问数据,有时可以更改数据。因此,您提供了名为访问器和赋值器的公共方法,以允许它们这样做,也称为getter和setter。另外,您可能希望他们以您选择的格式查看整个对象,这样您就可以定义一个ToString方法;这将返回一个表示对象数据的字符串。

    结构略有不同。

    这是一个班。

    它是一个物体。

    但它通常在另一个类中是私有的;因为节点在树中是私有的,树的用户不应该直接访问它。但是,在树对象内部,节点数据成员是公开可见的。节点本身不需要访问器和赋值函数,因为这些函数受树对象的信任和保护。

    要研究的关键字:封装、可见性修饰符