【问题标题】:How abstraction and encapsulation differ?抽象和封装有何不同?
【发布时间】:2013-06-01 01:53:30
【问题描述】:

我正在准备面试,并决定复习我的 OOP 概念。 有数百篇文章可用,但似乎每篇文章对它们的描述都不同。 Some

抽象是“识别具有 系统变异;抽象代表通用模式 并提供了一种指定使用哪个变体的方法”(Richard 加布里埃尔)。

并且是通过抽象类实现的。

一些other

抽象意味着只向客户展示必要的细节 对象

假设您的 Employee 类中有一个方法“CalculateSalary”, 它以 EmployeeId 作为参数并返回员工的薪水 当前月份的员工作为整数值。现在如果有人 想用那个方法。他不需要关心员工如何 对象计算工资?他唯一需要担心的是 方法名称、输入参数和结果格式 会员,

我一次又一次地用谷歌搜索,但似乎没有一个结果给我一个正确的答案。 现在,封装适合所有这些吗? 我搜索并找到了stack overflow question。甚至这些问题的答案也令人困惑 Here,上面写着

封装是一种用作抽象一部分的策略。封装 指对象的状态——对象封装了它们的状态和 把它从外面隐藏起来;类的外部用户与之交互 通过它的方法,但不能直接访问类状态。所以 该类抽象出与其相关的实现细节 状态。

here 另一位知名成员说,

它们是不同的概念。

抽象是提炼所有 对象的不需要/不重要的属性,只保留 最适合您的域的特征。

现在我搞砸了整个概念。我知道抽象类、继承、访问说明符等等。我只想知道在面试中被问及抽象和/或封装时我应该如何回答。

请不要将其标记为重复。我知道有几个类似的问题。但我想避免相互矛盾的解释之间的混淆。谁能推荐一个可靠的链接?除非它再次造成混乱,否则也欢迎指向 stackoverflow 问题的链接。 :)

编辑:我需要答案,有点面向 c#

【问题讨论】:

  • 所有这些答案再次让我感到困惑:(
  • 我要开始悬赏这个问题。
  • 问题是这些概念没有精确的定义,即使在面向对象的上下文中,这些词本身也有多重含义。如果你在采访中谈到这个,我希望这就足够了!
  • @MatthewWatson:If you talk about that in an interview, I would hope that would be sufficient! 我没听懂你。
  • 我希望this article 能对两者的区别给出一个清晰的概念...... :)

标签: c# oop encapsulation abstraction


【解决方案1】:

封装:使用 getter 和 setter 等隐藏数据。

抽象:使用抽象类和接口等隐藏实现

【讨论】:

  • 这个错误答案获得的赞成票数量简直惊人。尽管抽象类没有实现,但它们的目的不是抽象。他们的目的是继承,这是一个完全不同的面向对象分析和设计的原则。
  • 你的意思是说没有抽象类或者接口就不能实现抽象吗?这绝对是一个非常错误的答案。
  • 每个函数都是一个封装检查this也检查手机示例this
  • OOP 中的抽象概念与此处所述的概念不同。要清楚比较 OOP 中的抽象和封装,请访问我的博文@rajndev.medium.com/…
【解决方案2】:

据我所知,封装本身就是隐藏类的数据,并且只有在必须从外部世界访问它们时才可以通过 setter/getter 访问它们。

抽象是为自己设计的类。

意思是,你如何创建你的类树,哪些方法是通用方法,哪些是继承的,哪些可以被覆盖,哪些属性只能在私有级别或受保护,你如何建立你的类继承树,做您使用最终类、抽象类、接口实现。

抽象更多地放在OO设计阶段,而封装也进入开发阶段。

【讨论】:

    【解决方案3】:

    我认为它们是略有不同的概念,但通常它们是一起应用的。封装是一种向调用者隐藏实现细节的技术,而抽象更多是一种设计理念,涉及创建类似于熟悉的对象/过程的对象,以帮助理解。封装只是可用于创建抽象的众多技术之一。

    以“windows”为例。它们并不是传统意义上的真正的窗口,它们只是屏幕上的图形方块。但将它们视为窗口很有用。这是一个抽象。

    如果“windows API”隐藏了文本或图形在窗口边界内如何物理呈现的细节,这就是封装。

    【讨论】:

    • 你能提供一个来源来支持你的概念吗?
    • 这个 Wikipedia 页面:http://en.wikipedia.org/wiki/Abstraction_(computer_science) 包含一个很好的 Java 示例,其中有一个名为“animal”的类,它是一种抽象。该类封装了动物具有“EnergyReserves”这一事实——调用者不需要知道这一点。相反,调用者只知道动物是否“饿了”。 EnergyReserves 被隐藏(封装)。
    • 这是我会投票的答案。但是,抽象示例对我来说不是很清楚,因为我希望看到一些抽象类或接口的东西,而不是一般的英语抽象概念。
    【解决方案4】:

    我总是在抽象的上下文中提出一个例子;汽车上的自动变速箱与手动变速箱。手动变速箱隐藏了一些换档的工作原理,但作为驾驶员,您仍然需要离合器和换档。自动变速器封装了换档的所有细节,也就是对你隐藏起来,因此它是对换档过程的更高抽象。

    【讨论】:

    • “自动变速器封装了换档的所有细节,即对你隐藏它”......实际上这就是为什么我会说汽车上的自动变速器与手动变速器是封装的一个很好的例子!而不是为了抽象。自动变速器不是手动变速器的抽象,它是另一个概念。如果您说汽车需要动力传输系统而忽略了驾驶员的实际界面(自动或手动),那么抽象就是。
    • 我明白你的意思。不过,在我看来,抽象和封装是相关的。如果你没有任何封装,你就没有任何抽象。通过封装可以找到更多抽象的概念,因此自动变速器比手动变速器是一个更好的抽象。也许你甚至不需要传动的概念,也许你只需要汽车前进的概念,它将封装汽车的所有内部工作原理。
    • 我完全同意你最后的说法。您可以将所有内容抽象出来,直到没人知道它实际上是什么为止。但我不同意你的第一个说法。您可以在没有抽象的情况下练习封装,例如通过制作一个只有一个大类的程序来处理所有事情并隐藏客户端的所有实现细节。没有封装的抽象也可以通过将程序从小类中制作出来并且具有单一职责但不隐藏任何实现细节或任何无效状态。
    【解决方案5】:

    我的 2c

    封装的目的是对类的用户隐藏实现细节,例如如果您在班级内部保留一个 std::list 项目,然后决定 std::vector 会更有效,您可以在用户不关心的情况下更改它。也就是说,您与任一 stl 容器交互的方式都归功于抽象,例如,列表和向量都可以使用类似的方法(迭代器)以相同的方式遍历。

    【讨论】:

    • 恕我直言,我会坚持我的解释 :) 即封装对用户隐藏,抽象更多的是一个常见概念,例如几个类共享的接口。
    • 嗯..这就是我在问题本身中所说的。每个人对这个话题都有自己的概念:)。你能提供一个可信的链接来支持你的解释吗?
    • 不,我没有链接,这是我从 Code Complete 等各种书籍中理解的。
    • c-sharpcorner.com/UploadFile/tusharkantagarwal/… 请检查这两个链接并告诉我接受什么
    【解决方案6】:

    抽象意味着只向对象的客户显示必要的细节

    其实就是封装。另请参阅维基百科文章的第一部分,以免被封装和数据隐藏所混淆。 http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)

    请记住,简单地将所有类成员 1:1 隐藏在属性后面根本不是封装。封装就是保护不变量和隐藏实现细节。

    这里有一篇很好的文章。 http://blog.ploeh.dk/2012/11/27/Encapsulationofproperties/ 还请查看该文章中链接的文章。

    类、属性和访问修饰符是在 c# 中提供封装的工具。

    您进行封装是为了降低复杂性。

    抽象是“识别具有系统变化的常见模式的过程;抽象代表常见模式并提供一种指定使用哪种变化的方法”(Richard Gabriel)。

    是的,这是一个很好的抽象定义。

    它们是不同的概念。 抽象是提炼对象所有不需要/不重要的属性并仅保留最适合您的领域的特征的过程。

    是的,它们是不同的概念。请记住,抽象实际上与使对象仅适用于您的域相反。这是为了使对象适合一般领域!

    如果你有一个实际的问题并提供了一个具体的解决方案,你可以使用抽象来形式化一个更通用的解决方案,它也可以解决更多具有相同常见模式的问题。这样,您可以提高组件的可重用性,或者使用其他程序员为同一域甚至不同域制作的组件。

    好的例子是 .net 框架提供的类,例如列表或集合。这些是非常抽象的类,您几乎可以在任何地方和许多领域中使用它们。想象一下,如果 .net 只实现了一个 EmployeeList 类和一个 CompanyList,它只能保存具有特定属性的员工和公司列表。在很多情况下,这样的类是无用的。例如,如果您必须重新实现 CarList 的整个功能,那将是多么痛苦。所以“列表”是从员工、公司和汽车中抽象出来的。 List 本身是一个抽象概念,可以由它自己的类来实现。

    接口、抽象类或继承和多态是在 c# 中提供抽象的工具。

    您进行抽象是为了提供可重用性。

    【讨论】:

    • stackoverflow.com/a/8960961/2401223 上面写着Encapsulation is a strategy used as part of abstraction
    • 我明白了.. 抽象背后有不同的含义。维基百科在“抽象”和“抽象原理”之间也有所不同。坚持 Matthew Watson 在 cmets 中告诉您的内容“问题是这些概念没有精确的定义,即使在面向对象的上下文中,这些词本身也有多种含义。”只要你能在面试中给他们一个或多个含义,一切都会好起来的:)
    • 谢谢。我会听从这个建议:)
    • “List 本身就是一个抽象概念”——我认为它偏离了抽象的编程概念,因为 List 类如果不是接口,也是一个具体的类。
    • 我不这么认为。接口不是抽象,但可以是。具体类不是抽象,但可以。 blog.ploeh.dk/2010/12/02/Interfacesarenotabstractions .net 中还有一个 IList 接口。你可能是正确的想法,我想我将域中的抽象和代码中的抽象混为一谈。
    【解决方案7】:

    我将尝试以一种简单的方式来演示封装和抽象..让我们看看..

    • 将数据和函数封装成一个单元(称为 class) 被称为封装。封装包含和隐藏 关于对象的信息,例如内部数据结构和 代码。

    封装是-

    • 隐藏复杂性,
    • 将数据和函数绑定在一起,
    • 将复杂方法设为私有,
    • 将实例变量设为私有,
    • 向最终用户隐藏不必要的数据和功能。

    封装实现抽象。

    而抽象是 -

    • 显示什么是必要的,
    • 数据需要从最终用户那里抽象出来,

    让我们看一个例子-

    下图显示了“要添加到数据库中的客户详细信息”的 GUI。

    通过查看图像,我们可以说我们需要一个客户类。

    第 1 步:我的客户类别需要什么?

    • 2 个变量用于存储客户代码和客户名称。

    • 1 将客户代码和客户名称添加到数据库中的函数。

      namespace CustomerContent
        {
           public class Customer
           {
               public string CustomerCode = "";
               public string CustomerName = "";
               public void ADD()
               {
                  //my DB code will go here
               }
    

    现在只有 ADD 方法不能单独在这里工作。

    步骤-2:验证将如何工作,ADD 函数作用?

    我们将需要数据库连接代码和验证代码(额外方法)。

         public bool Validate()
         {
        //Granular Customer Code and Name
        return true;
         }
    
         public bool CreateDBObject()
         {
        //DB Connection Code
        return true;
         }
    
    
    class Program
    {
       static void main(String[] args)
       {
         CustomerComponent.Customer obj = new CustomerComponent.Customer;
    
         obj.CustomerCode = "s001";
         obj.CustomerName = "Mac";
    
         obj.Validate();
         obj.CreateDBObject();
    
         obj.ADD();
        }
    }
    

    现在不需要向最终用户显示额外方法(Validate();CreateDBObject() [复杂和额外方法])。最终用户只需要查看和了解客户代码、客户名称和添加按钮哪个会添加记录。最终用户不关心它将如何将数据添加到数据库中?。

    步骤-3:将不涉及最终用户交互的额外复杂方法私有化。

    因此,将那些 Complicated 和 Extra 方法设为 Private 而不是 Public(即隐藏这些方法)并从类 Program 中的 main 中删除 obj.Validate(); obj.CreateDBObject(); 我们实现了封装。

    换句话说,为最终用户简化接口就是封装。

    所以现在完整的代码如下 -

     namespace CustomerContent
     {
         public class Customer
         {
            public string CustomerCode = "";
            public string CustomerName = "";
    
            public void ADD()
            {
               //my DB code will go here
            }
    
            private bool Validate()
            {
               //Granular Customer Code and Name
               return true;
            }
    
            private bool CreateDBObject()
            {
               //DB Connection Code
               return true;
            }
    
    
      class Program
      {
         static void main(String[] args)
         {
            CustomerComponent.Customer obj = new CustomerComponent.Customer;
    
            obj.CustomerCode = "s001";
    
            obj.CustomerName = "Mac";
    
            obj.ADD();
       }
    }
    

    总结:

    步骤-1:我的客户类需要什么?是抽象

    步骤-3:步骤-3:将不涉及最终用户交互的额外复杂方法私有化为封装

    附注- 上面的代码既难又快。

    更新: 此链接上有一个视频来解释示例: What is the difference between Abstraction and Encapsulation

    【讨论】:

    • 现在如何调用Validate()CreateDBObject()
    • 那你怎么称呼Validate()CreateDBObject()...我同意@varsha。
    • public void ADD() { Validate(); CreateDBObject(); }
    • 误导性答案。
    【解决方案8】:

    我是这样想的,封装隐藏了某事完成的方式。这可以是一个或多个动作。

    抽象与“为什么”有关,我首先将其封装。

    我基本上是在告诉客户“你不需要知道我如何处理付款和计算运费等。我只是想让你告诉我你想‘结账’,我会处理详细信息。”

    这样我通过概括(抽象)到 Checkout 请求中来封装细节。

    我真的认为抽象和封装是相辅相成的。

    【讨论】:

      【解决方案9】:

      以下是一学期的课程,分几段。

      面向对象的分析和设计 (OOAD) 实际上不仅基于两个,而且基于四个原则。它们是:

      • 抽象: 意味着您只包含应用程序中需要的实体的那些功能。因此,如果每个银行账户都有一个开户日期,但您的应用程序不需要知道一个账户的开户日期,那么您只需在面向对象设计中添加 OpeningDate 字段(属于BankAccount 类)。 OOAD 中的抽象与 OOP 中的抽象类无关。

        根据抽象原理,您的实体是它们在现实世界中的抽象。这样,您就可以将银行账户的抽象设计降低到您的应用程序所需的详细程度。

      • 继承: 与其说是实际原理,不如说是一种编码技巧。它使您免于重写您在其他地方编写的那些功能。但是,我们的想法是,您正在编写的新代码和您想要重用的旧代码之间必须存在关系。否则,没有人会阻止你编写一个继承自 BankAccountAnimal 类,即使它完全没有意义。

        就像你可以继承父母的财富一样,你也可以继承父类的字段和方法。因此,获取父类所拥有的所有内容,然后在需要时添加更多内容,这就是继承。不要在面向对象设计中寻找继承。继承自然会出现。

      • 多态性: 是继承的结果。从父类继承方法很有用,但如果情况需要,能够修改方法,则是多态性。您可以在子类中实现与父类中完全相同的签名的方法,以便在调用时执行子类中的方法。这就是多态的原理。

      • 封装:意味着将相关功能捆绑在一起,并在必要时提供访问权限。封装是面向对象设计中有意义的类设计的基础,作者:

        • 将相关数据和方法放在一起;并且,
        • 仅公开与外部实体运行相关的数据和方法。

      另一个简化的答案是here


      认为“OOAD 的抽象导致 OOP 的抽象关键字”的人... 那是不正确的。

      示例:当您使用面向对象原则在应用程序中设计大学时,您只设计了大学的“抽象”。即使几乎每所大学通常都有一台自动取款机,但如果您的申请不需要它,您可能不会考虑这一事实。现在虽然您只设计了大学的抽象,但您不需要将 abstract 放在类声明中。你的大学抽象设计将是你申请中的一个普通类。

      【讨论】:

        【解决方案10】:

        Image source

        抽象:由猫的左上角和右上角的图像勾勒出来。外科医生和老妇人设计(或想象)动物的方式不同。同样,您可以根据应用程序的需要在 Cat 类中放置不同的功能。每只猫都有肝脏、膀胱、心脏和肺,但如果您只需要猫发出“咕噜声”,您将把应用程序的猫抽象为左上角而不是右上角的设计。

        封装:由站在桌子上的猫勾勒出来。这就是猫以外的每个人都应该看到的猫。他们不必担心 cat 的实际实现是左上的还是右上的,或者两者兼而有之。


        PS:就同一问题转至here 以收听完整的故事。

        【讨论】:

        • 迄今为止我发现的 OOP 中抽象的最佳可视化示例,谢谢
        【解决方案11】:

        封装:隐藏实现细节(注意:数据和/或方法),以便他们只能访问外部可读/可写/可用的内容,其他所有内容都是直接“不可触及”的。

        抽象:这有时专门指一种无法实例化的类型,它为其他可以实例化的类型提供模板,通常通过子类化。更一般地说,“抽象”是指制作/拥有不那么详细、不那么具体、不那么细化的东西。

        概念之间有一些相似之处,重叠,但记住它的最佳方式是这样的:封装更多的是隐藏细节,而抽象更多的是概括 详情。

        【讨论】:

          【解决方案12】:

          抽象和封装是令人困惑的术语并且相互依赖。 举个例子吧:

          public class Person
              {
                  private int Id { get; set; }
                  private string Name { get; set; }
                  private string CustomName()
                  {
                      return "Name:- " + Name + " and Id is:- " + Id;
                  }
              }
          

          当您创建 Person 类时,您通过将属性和函数一起编写(Id、Name、CustomName)来进行封装。当您将此类公开给客户端时,您执行抽象

          Person p = new Person();
          p.CustomName();
          

          您的客户对这个函数中的 Id 和 Name 一无所知。 现在,如果您的客户也想知道姓氏,而不会干扰函数调用。您可以像这样在 Person 类中再添加一个属性来进行封装。

          public class Person
                  {
                      private int Id { get; set; }
                      private string Name { get; set; }
                      private string LastName {get; set;}
                      public string CustomName()
                      {
                          return "Name:- " + Name + " and Id is:- " + Id + "last name:- " + LastName;
                      }
                  }
          

          看,即使在类中添加了额外的属性,您的客户也不知道您对代码做了什么。这是您进行抽象的地方。

          【讨论】:

            【解决方案13】:

            抽象

            在 Java 中,抽象意味着将信息隐藏到现实世界中。它建立了双方之间的合同,以告知“我们应该如何使用服务”。

            例如,在 API 开发中,只有服务的抽象信息被公开,而不是实际的实现。 java中的接口可以很好的帮助实现这个概念。

            接口提供各方之间的合同,例如生产者和消费者。生产者在不让消费者知道产品是如何制造的情况下生产商品。但是,通过界面,Producer 让所有消费者知道可以购买什么产品。在抽象的帮助下,生产者可以将产品推销给他们的消费者。

            封装:

            封装是抽象的下一层。同一产品公司尝试屏蔽来自其他生产组的信息。例如,如果一家公司生产葡萄酒和巧克力,则封装有助于屏蔽每种产品如何相互制造的信息。

            1. 如果我有单独的包装,一个用于葡萄酒,另一个用于 巧克力,如果所有的类都在包中声明为 默认访问修饰符,我们提供包级封装 适用于所有课程。
            2. 在包中,如果我们将每个类字段(成员字段)声明为 私有并具有访问这些字段的公共方法,这种方式 为这些字段提供类级别的封装

            【讨论】:

              【解决方案14】:

              封装

              在 OOP 中,封装是将相关数据和行为组合到一个内聚的、功能性的、自包含的类单元中的过程,在以下方面对用户“隐藏”敏感数据私有访问修饰符。

              C#代码示例:

              下面的代码 sn-p 说明了封装与模拟 ATM 机的类。请注意,它只包含相关的属性和方法,并且像 cashLevel 这样的敏感属性是通过私有访问修饰符对外部访问隐藏的:

              public class ATMMachine
                  {
                      private string customerName;
                      private string accountNumber;
                      private decimal totalCashInAtm;
              
                      public void WithdrawMoney()
                      {
                          //dispense cash to the customer and print the balance on the recepit
                      }
              
                      public void DepositMoney()
                      {
                          //obtain the deposit from the customer and add it to the account
                      }
                  }
              

              抽象

              另一方面,在 OOP 中,抽象是在使用对象时仅公开与用户相关的内容并“隐藏”使这项有用工作成为可能的不相关且复杂的机制的过程。这是通过分别使用 public 和 private 访问修饰符来实现的。

              C#代码示例:

              以汽车为例。例如,您无需了解电气和机械系统在幕后的运作方式。您只需将钥匙插入点火装置即可启动发动机并行驶。

                public class Car
                  {
                      //Data relevant to drivers
                      public int speed;
                      public int rpm;
                      public int engineTemp;
                      
                      //Data for the internal system
                      private int batteryVoltage;
                      private int fuelInjectionRate;
              
                      //Public user interface
                      public void StartEngine()
                      {
                          //driver turned the key
              
                          ActivateStarter();
                      }
              
                      //Making the car move forward
                      public void Accelerate()
                      {
                          //driver pressed the gas pedal
              
                          TurnPistons();
                      }
              
                      //Starting the engine
                      private void ActivateStarter()
                      {
                          //use the batteryVoltage value and other engine data to activate the starter
                      }
              
                      //Operation of engine
                      private void TurnPistons()
                      {
                          //use the fuelInjectionRate and other information to activate the pistons
                      }
                  }
              

              在旁注中,抽象中使用的“隐藏”的含义与封装中使用的含义有细微的区别。在封装中,私有访问修饰符用于“隐藏”或限制对敏感数据的访问。但是,在抽象中,“隐藏”是指使用私有访问修饰符来限制用户访问用户不需要知道的不相关的属性和方法

              有关 C# 中的抽象与封装的完整解释,follow this link 我的 Medium 文章。

              【讨论】:

                猜你喜欢
                • 2012-05-24
                • 2014-09-21
                • 2012-02-16
                • 1970-01-01
                • 2011-04-23
                • 2017-05-01
                • 1970-01-01
                • 1970-01-01
                • 2019-11-28
                相关资源
                最近更新 更多