【问题标题】:What is the difference between abstraction and encapsulation? [duplicate]抽象和封装有什么区别? [复制]
【发布时间】:2011-02-11 07:44:50
【问题描述】:

可能重复:
difference between abstraction and encapsulation?

Java 中的封装和抽象到底有什么区别?任何简短的例子也将不胜感激。

【问题讨论】:

标签: java oop


【解决方案1】:

抽象和封装是两种很好的味道,在一起味道很好。

封装 正在最大限度地减少您向代码用户公开的内容。该“用户”可能是您的代码的其余部分,或者使用您发布的代码的任何人。

封装有一些明确的好处:

  • 您的代码的用户不依赖于您的程序中可能发生变化的部分。当您更改程序时,他们不必更改代码
  • 您可以更准确地控制代码和状态在程序生命周期内的变化方式。您必须处理的场景更少,需要修复的意外问题也会更少

我不懂Java,但是这里有一个用C#封装的小例子:

public class Giraffe
{
    public Giraffe(int heightInFeet)
    {
        this.heightInFeet = heightInFeet;
        this.numberOfSpots = heightInFeet * 72;
    }

    public override string ToString()
    {
        return "Height: " + heightInFeet + " feet"
            + " Number of Spots: " + numberOfSpots;
    }

    private int heightInFeet;
    private int numberOfSpots;
}

不是暴露numberOfSpots,而是封装在类中,并通过ToString方法暴露。

抽象使用扩展点让选择推迟到运行确切代码的不同部分。该选择可以在您程序的其他地方、另一个程序中或在运行时动态进行。

抽象还有很多好处:

  • 当您更改实现抽象的代码时,抽象的用户不必更改他们的代码。只要抽象不改变,用户就不必改变他们的代码。
  • 当您编写使用抽象的代码时,您可以编写一次代码,该代码可针对任何实现该抽象的新代码重复使用。您可以编写更少的代码来做更多的事情。

C# 中一个高度使用的抽象是IEnumerable。列表、数组、字典和任何其他类型的集合类都实现了IEnumerableforeach 循环结构和整个 LINQ 库都基于该抽象:

public IEnumerable<int> GetSomeCollection()
{
    // This could return any type of int collection.  Here it returns an array
    return new int[] { 5, 12, 7, 14, 2, 3, 7, 99 };
}

IEnumerable<int> someCollectionOfInts = GetSomeCollection();

IEnumerable<string> itemsLessThanFive = from i in someCollectionOfInts
                                        where i < 5
                                        select i.ToString();

foreach(string item in itemsLessThanFive)
{
    Console.WriteLine(item);
}

您也可以轻松编写自己的抽象:

public interface IAnimal
{
    bool IsHealthy { get; }
    void Eat(IAnimal otherAnimal);
}

public class Lion : IAnimal
{
    public Lion()
    {
        this.isHealthy = true;
    }

    public bool IsHealthy
    {
        get { return isHealthy; }
    }

    void Eat(IAnimal otherAnimal)
    {
        if(otherAnimal.IsHealthy && !(otherAnimal is SlimeMold))
        {
            isHealthy = true;
        }
        else
        {
            isHealthy = false;
        }
    }

    private bool isHealthy;
}

IAnimal someAnimal = PullAnAnimalOutOfAWoodenCrate();

Console.WriteLine("The animal is healthy?: " + someAnimal.IsHealthy);

您可以同时使用两者,就像我对 IAnimalIsHealthy 所做的那样。 IAnimal 是一个抽象,只有 get 访问器,IsHealthy 上没有 set 访问器是封装。

【讨论】:

    【解决方案2】:

    这两个概念完全不同。

    抽象是使基类“抽象”然后扩展其功能的做法。抽象类是在具体事物中不存在的东西。它的唯一目的是扩展。想想你是否正在编写类来代表不同的物种。您所有不同的物种都可能扩展一个抽象的 Animal 类,因为它们都将共享作为动物的共同属性。但是,您永远不会实例化 Animal 对象,因为您在世界上看到的每一种动物都是松鼠、狗或鱼……或者是该基础抽象动物类的某种具体实现。

    封装是将类变量设为私有,然后允许从 get 和 set 方法访问它们的做法。这样做的目的是将您的数据访问方式和实现方式分开。例如,如果您有某个变量有一个要求,即每次更改它时,它也会将第二个变量增加 1,那么您将封装该功能;这样您的代码就更可靠了,因为您不必记住每次访问原始变量时都遵守该规则。

    如果您想要特定的代码示例,我建议您只进行谷歌搜索,因为有很多类似的示例可用。这里有两个:

    http://www.tutorialspoint.com/java/java_abstraction.htm http://www.tutorialspoint.com/java/java_encapsulation.htm

    【讨论】:

    • 嗨 Herminator 感谢您的回答。我们可以称 DTO 是封装的一个示例。
    【解决方案3】:

    封装是为了保护你的成员变量或方法不受外界影响。

    抽象是有具体实现的方式。那就是用户不知道要使用哪个实现。

    【讨论】:

      【解决方案4】:

      封装是抽象的一部分。抽象的概念是创建一个对象来表示另一个对象。通常,原始对象比抽象对象更复杂。因此,抽象是一种表示,通常作为记忆的辅助,用于术语/交流等。可以这样想:抽象艺术是其他事物的表示。方向盘、换档和 2/3 踏板是汽车工作方式的抽象。

      基本上,抽象允许您将具有很多细节的复杂事物表示为更简单的事物。在我看来,这与认知科学中的“分块”有关。我们无法将复杂的事情留在脑海中,所以我们通过抽象来简化,然后使用抽象。设计模式是另一个很好的例子。与其谈论细节,我们可以谈论命令、状态或策略模式等。

      封装是形成/创建抽象的一部分。对象的接口越小,就越容易抽象。你不需要知道发动机和变速箱是如何驱动汽车的,你只需要了解它们的抽象概念(换档和加速器)。引擎和变速箱的细节被封装(到接口中)以创建抽象。

      抽象需要封装,因为抽象不能处理所有真实的细节和复杂性(否则它不是抽象)。因此,换档是变速箱的不完整表示(或模型),但对于日常使用来说足够完整。封装可以被认为是“隐藏细节”,这是创建更简单的表示所必需的。

      讨论“接口”的概念也很重要。在大多数情况下,在这种情况下,术语“接口”和“抽象”更难互换。接口是系统的一部分,用户与之打交道或交互。汽车的接口是方向盘、换档和踏板等。抽象产生一个接口。您不直接处理发动机/变速箱,而是处理它们各自的接口。

      封装的另一个原因是因为我们正在处理不完整的模型/抽象,我们不了解原始的全部复杂性,并且不能信任处理所有变量(因为我们不了解完整模型)。这对于解耦很重要,因为如果没有抽象,交互的组件会彼此了解太多。想想看,因为每辆车都有方向盘、踏板和换档装置,你可以驾驶任何汽车,不管发动机类型等。此外,变速箱是从发动机中抽象出来的。否则每个定制引擎都需要定制变速箱。

      同样,类是一种抽象。类通过它的接口——类的公共成员来表示一些复杂的模型。该接口是通过封装创建的。该类向其合作者提供了其更复杂实现的简化接口。您也可以将其视为“需要知道”的情况。类的协作者不需要确切地知道它是如何工作的。就像你不需要知道引擎是如何驱动汽车的一样。

      封装、接口和抽象在凝聚力和耦合以及代码维护方面发挥着关键作用。如果你没有创建好的抽象,并且违反了“需要知道”的原则,那么你的代码就会变得纠缠不清、脆弱,并且是改变的噩梦,因为没有“缓冲”。 OO 的“告诉不要问”的概念也与此有关。

      【讨论】:

        猜你喜欢
        • 2014-09-21
        • 1970-01-01
        • 2010-11-13
        • 2013-02-17
        • 2013-02-17
        • 2018-11-04
        • 2011-01-28
        相关资源
        最近更新 更多