抽象和封装有何不同?


74

我正在准备面试,并决定重新整理我的OOP概念。有数百篇文章可用,但似乎每个文章对它们的描述都不相同。 有人

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

并通过抽象类实现。

一些其他的

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

假设您的Employee类中有一个方法“ CalculateSalary”,该方法将EmployeeId作为参数,并以整数值返回当月员工的薪水。现在,如果有人想使用该方法。他不需要关心Employee对象如何计算薪水?他唯一需要关注的是方法的名称,其输入参数和所得成员的格式,

我一次又一次地搜索,结果似乎都没有给我一个正确的答案。 现在,封装在所有这些方面适合什么地方? 我搜索并发现了堆栈溢出问题。它说,即使是这些问题的答案也使这里感到困惑。

封装是用作抽象的一部分的策略。封装是指对象的状态-对象将其状态封装起来并从外部隐藏起来;类的外部用户通过其方法与之交互,但无法直接访问类的状态。因此,该类将与状态相关的实现细节抽象出来。

这里的另一个知名的成员说,

它们是不同的概念。

抽象是完善对象的所有不需要/不重要属性并仅保留最适合您的域的特征的过程。

现在我把整个概念弄糊涂了。我了解抽象类,继承,访问说明符以及所有内容。我只想知道在面试中被问到抽象和/或封装时应该如何回答。

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

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


4
所有这些答案再次使我感到困惑:(
阿帕兰

3
我将开始悬赏这个问题。
Aparan

2
问题在于这些概念没有精确的定义,并且单词本身甚至在面向对象的上下文中也具有多种含义。如果您在采访中谈到这一点,我希望这足够了!
马修·沃森

2
@MatthewWatson:If you talk about that in an interview, I would hope that would be sufficient! 我不明白你的意思。
Aparan

1
我希望本文 能对两者的区别给出一个清晰的想法... :)
好奇

Answers:


50

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

其实就是封装。另请参阅Wikipedia文章的第一部分,以免被封装和数据隐藏所混淆。http://en.wikipedia.org/wiki/Encapsulation_(面向对象编程)

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

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

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

您可以进行封装以降低复杂性。

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

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

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

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

如果您有实际问题并提供特定的解决方案,则可以使用抽象形式来规范化一个更通用的解决方案,该解决方案也可以解决具有相同通用模式的更多问题。这样,您可以提高组件的可重用性,或使用由其他程序员针对相同域甚至不同域制作的组件。

.net框架提供的类就是很好的例子,例如列表或集合。这些是非常抽象的类,您几乎可以在任何地方和许多领域中使用。试想一下,如果.net仅实现了一个EmployeeList类和一个CompanyList,该类只能容纳具有特定属性的员工和公司列表。这样的类在很多情况下是没有用的。例如,如果您必须重新实现CarList的全部功能,那将是多么痛苦。因此,“列表”与员工,公司和汽车无关。List本身是一个抽象概念,可以通过其自己的类来实现。

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

您进行抽象以提供可重用性。


2
stackoverflow.com/a/8960961/2401223它说Encapsulation is a strategy used as part of abstraction
Aparan

我知道..抽象背后有不同的含义。维基百科在“抽象”和“抽象原理”之间也有所不同。坚持Matthew Watson在评论中告诉您的内容:“问题是这些概念没有精确的定义,即使在面向对象的上下文中,单词本身也具有多种含义。” 只要您能在面试中给他们一种或多种含义,一切都会好起来的:)
Egi

谢谢。我将遵循该建议:)
阿帕兰

“列表本身是一个抽象概念”-我认为它与抽象的编程概念有所不同,因为List类是一个具体的类(如果不是接口的话)。
user30478

我不这么认为。接口不是抽象,但可以是抽象。具体的类不是抽象的,而是可以的。blog.ploeh.dk/2010/12/02/Interfacesarenotabstractions .net中也有一个IList接口。您可能是正确的想法,我想我正在将域中的抽象与代码中的抽象混合在一起。
埃吉

94

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

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


6
这个错误答案得到的赞成票数目简直令人惊讶。尽管抽象类没有实现,但它们的目的不是Abstraction。它们的目的是继承,它是面向对象的分析和设计的完全不同的原理。
displayName

2
您是说没有抽象类或接口就无法实现抽象吗?这绝对是非常错误的答案。
维克拉姆

1
每一个功能是封装检查还要检查这个手机例子
Shaiju T

50

抽象和封装示例 图片来源

抽象:由猫的左上和右上图像概述。外科医生和老太太对动物的设计(或形象化)不同。同样,您可以根据应用程序的需求在Cat类中放置不同的功能。每只猫都有肝脏,膀胱,心脏和肺,但是如果您只需要“呼pur”它,则可以将应用程序的猫抽象到左上角而不是右上角的设计。

封装:由站在桌子上的猫勾勒出轮廓。那就是猫外面的每个人都应该把猫看成是。他们不必担心猫的实际实现是左上角的猫还是右上角的猫,甚至是两者的组合。


PS:转到这里对这个同样的问题听到完整的故事。


7
到目前为止,我已经找到了OOP中最好的抽象视觉示例,谢谢您
Ricardo Dias Morais

27

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

  • 将数据和功能包装到单个单元(称为类)中的过程称为封装。包含和隐藏有关对象的信息的封装,例如内部数据结构和代码。

封装是-

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

封装实现抽象。

而且抽象是-

  • 显示必要的内容
  • 数据需要从最终用户中提取,

让我们看一个例子-

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

客户屏幕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();
    }
}

现在,无需向最终用户显示Extra方法(Validate()CreateDBObject()[复杂方法和Extra方法])。最终用户只需要查看和了解将添加记录的客户代码,客户名称和ADD按钮。不在乎如何将数据添加到数据库?

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

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

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

因此,现在完整的代码如下所示-

 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:将不涉及最终用户交互的额外复杂方法私有化

PS-上面的代码很难实现。

更新:该链接上有一个视频来解释该示例: 抽象与封装之间有什么区别


1
现在怎么会Validate()CreateDBObject()被调用?
varsha

您怎么打Validate()CreateDBObject()然后...我同意@varsha。
Krish

1
public void ADD() { Validate(); CreateDBObject(); }
维克拉姆

误导性答案。
user30478 '18 -10-31

11

以下是几学期的一学期课程。

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

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

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

  • 继承:更多的是编码技巧,而不是实际的原理。它使您不必重写在其他地方编写的那些功能。但是,这种想法是,您正在编写的新代码和您想要重用的旧代码之间必须存在某种关系。否则,没有人会阻止您编写从BankAccount继承的Animal类,即使它完全没有意义。

    就像您可以继承父母的财产一样,您可以从父类继承字段和方法。因此,继承父类拥有的所有东西,然后根据需要添加更多东西。不要在面向对象的设计中寻找继承。继承自然会呈现出来。

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

  • 封装:意味着将相关功能捆绑在一起,并访问有需要的人。封装是面向对象设计中有意义的类设计基础,方法是:

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

另一个简化的答案在这里


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

示例:当您使用面向对象的原理在应用程序中设计大学时,您只会设计大学的“抽象”。即使几乎每所大学通常都有一个自动提款机,但如果您的应用程序不需要它,您可能不会合并这个事实。现在,尽管您仅设计了大学的抽象概念,但您无需输入abstract班级声明。您大学的抽象设计将成为您应用程序中的一门普通课。


10

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

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

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


您能否提供支持您的概念的资源?
Aparan

此Wikipedia页面:http : //en.wikipedia.org/wiki/Abstraction_(computer_science)包含Java中的一个很好的示例,其中有一个名为“ animal”的类是一个抽象。该类封装了动物具有“ EnergyReserves”的事实-调用者不需要知道这一点。而是,呼叫者仅知道动物是否“很饿”。EnergyReserves隐藏(封装)。
Mike Panter

这就是我将投票的答案。但是,由于我希望看到一些抽象类或接口的东西,而不是一般的英语抽象概念,所以抽象示例对我来说不是很清楚。
user30478 '18

4

我的2c

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


2
在所有适当的尊重下,我将坚持我的解释:),即避免向用户隐瞒,更多地抽象一些通用概念,例如几个类共享的接口。
AndersK 2013年

1
嗯..这就是我在提问中所说的。每个人对此主题都有自己的概念:)。您能否提供可靠的链接来支持您的解释?
Aparan


不,我没有链接,这是我从《代码完成》等各种书籍中了解的链接。
AndersK 2013年

c-sharpcorner.com/UploadFile/tusharkantagarwal/… 请检查这两个链接,并告诉我要接受的内容
Aparan 2013年

3

在抽象的背景下,我总是想到一个例子。汽车的自动变速箱与手动变速箱。手动变速箱隐藏了一些变速齿轮的工作原理,但您仍然必须作为驾驶员来抓紧和换挡。自动变速箱封装了变速齿轮的所有细节,即对您隐藏了所有细节,因此它是变速齿轮过程的更高抽象。


“自动变速箱封装了变速齿轮的所有细节,即对您隐藏了所有细节”……实际上,这就是为什么我要说汽车自动变速箱与手动变速箱是成功的典范!而不是抽象。自动变速箱不是手动变速箱的抽象,而是另一个概念。如果您说汽车需要动力传输系统而忽略了驾驶员的实际界面(自动或手动),那么它就是一个抽象。
Egi 2013年

我明白你的意思了。在我看来,抽象和封装是相关的。如果没有任何封装,则没有任何抽象。通过封装,您可以找到更多抽象概念,因此自动传输比手动传输更好。也许您甚至不需要变速器的概念,也许您只需要前进的汽车的概念,它可以封装汽车的所有内部工作原理。
洛伦兹·韦德勒

1
我完全同意你的最后声明。您可以将所有内容抽象化,直到没人知道它的实质。但我不同意你的第一句话。您可以在不进行抽象的情况下实践封装,例如,通过仅使用一个大类来处理所有内容并向客户端隐藏所有实现细节的程序。通过使程序脱离较小的类并具有单一职责但不隐藏任何实现细节或任何无效状态,也可以实现不进行封装的抽象。
Egi 2013年

1

封装:隐藏实现细节(注意:数据和/或方法),以便外部仅能明智地读取/写入/使用的内容才可供外部用户访问,而其他所有内容则直接“不可触摸”。

抽象:有时专门指无法实例化的类型,并且通常通过子类为其他可以提供的类型提供模板。更一般地,“抽象”是指制作/具有较不详细,较不具体,较不精细的内容。

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


1

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

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();

您的客户对此功能一无所知。现在,如果您的客户希望在不打扰函数调用的情况下也知道姓氏。您可以通过向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;
            }
        }

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


0

据我所知,封装是在自己内部隐藏类的数据,并且仅当必须从外部访问它们时才使它们可以通过setter / getter进行访问。

抽象本身就是类设计。

意味着,如何创建类树,哪些方法是常规方法,哪些方法可以被继承,哪些方法可以被覆盖,哪些属性仅在私有级别或受保护的情况下,您如何构建您的类继承树,您使用final类,抽象类,接口实现。

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


0

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

抽象与“为什么”我将其封装在第一位有关。

我基本上是在告诉客户“您不需要了解我如何处理付款和计算运费等。我只想让您告诉我您要'结帐',我会为您处理详细信息。”

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

我真的认为抽象和封装结合在一起。


0

抽象化

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

例如,在API开发中,只有服务的抽象信息才向世人展示,而不是实际的实现。Java接口可以很好地实现这一概念。

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

封装形式:

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

  1. 如果我有一个单独的包装用于葡萄酒,另一个单独的包装用于巧克力,并且如果所有类都在包装中声明为默认访问修饰符,那么我们将为所有类提供包装级别的封装。
  2. 在包中,如果我们声明每个提交的类(成员字段)为私有,并且具有访问这些字段的公共方法,则这种方式将对这些字段进行类级别的封装
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.