抽象和封装有什么不同?

我正在准备一次面试,并决定温习我的 OOP 概念。 有几百篇文章可供选择,但似乎每篇文章都有不同的描述。 有人说

抽象是“识别具有以下特征的常见模式的过程” 系统的变化; 抽象代表了共同的模式 并提供了一种指定使用哪种变体的方法”(理查德 加布里埃尔)。

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

一些 其他

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

还有

假设在 Employee 类中有一个方法“ CalculateSalary”, 它接受 EmployeeId 作为参数并返回 作为一个整数值。现在,如果有人 他不需要关心员工是如何使用这种方法的 对象计算工资? 他唯一需要关心的是 方法的名称、其输入参数和生成的 成员,

我谷歌了一遍又一遍,但没有一个结果给我一个合适的答案。 现在,封装在所有这些中的位置在哪里? 我搜索并找到了一个 堆栈溢出问题堆栈溢出问题。即使是这个问题的答案也令人困惑 这里写着

是作为抽象的一部分使用的策略 是指对象的状态-对象封装它们的状态和 从外部隐藏它; 类的外部用户与它交互 但是不能直接访问类的状态。所以 类抽象出与其 国家。

给你的另一位知名成员说,

它们是不同的概念。

抽象是将所有的 对象的不需要/不重要的属性,只保留 最适合你的领域的特性。

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

请不要将其标记为重复的。我知道有几个类似的问题。但是我想避免在相互矛盾的解释中产生混淆。有人能提供可靠的线索吗?指向堆栈溢出问题的链接也是受欢迎的,除非它再次造成混乱。:)

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

177829 次浏览

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

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

据我所知,封装将类的数据隐藏在类本身中,只有在必须从外部世界访问的情况下,才能通过 setter/getter 访问它们。

抽象本身就是类的设计。

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

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

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

例如,以“窗口”为例。它们不是传统意义上的窗口,它们只是屏幕上的图形方块。但把它们想象成窗户是有用的。那是抽象概念。

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

有一个例子总是在抽象的语境中被提到,那就是汽车上的自动手动变速器和自动驾驶。手动变速器隐藏了一些变速器的工作原理,但作为一个驾驶员,你仍然需要离合器和换档。自动变速器封装了所有变速器的细节,即对你隐藏它,因此它是变速器变速过程的更高抽象。

我的2C

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

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

实际上那是封装。为了不被封装和数据隐藏混淆,还可以参见 Wikipedia 文章的第一部分。http://en.wikipedia.org/wiki/Encapsulation_(object-oriented_programming)

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

这里有一篇关于这方面的好文章。 Http://blog.ploeh.dk/2012/11/27/encapsulationofproperties/ 也可以看看那篇文章中链接的文章。

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

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

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

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

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

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

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

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

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

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

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

  • 将数据和函数包装成单个单元(称为 类)称为封装 关于对象的信息,例如内部数据结构和 密码。

封装是

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

封装实现了抽象。

抽象是..

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

让我们看一个例子-

下图显示了“客户详细信息将添加到数据库”的图形用户界面。

Customer Screen 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()[复杂和额外的方法])。最终用户只需要看到和了解客户代码,客户名称和 ADD 按钮,将添加的记录。.最终用户不关心如何将数据添加到数据库?.

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

因此,使这些复杂和额外的方法作为私有而不是公共(即隐藏这些方法) ,并删除 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: 私有不涉及最终用户交互的额外和复杂的方法是 封装

附注-上面的代码是硬件和快速的

更新: 在这个链接上有一个视频来解释这个例子: 抽象和封装的区别是什么

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

抽象与“为什么”有关,我把它封装在第一位。

我基本上是告诉客户“你不需要知道很多关于我如何处理付款和计算运费等。我只是想让你告诉我你想要‘退房’,我会为你处理好细节。”

通过这种方式,我将细节概括(抽象)到 Checkout 请求中。

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

下面是一个学期长的课程提炼在几个段落。

面向对象的系统分析实际上是基于以下四个原则:

  • 抽象: 意味着您只包含应用程序中需要的实体的那些特性。因此,如果每个银行账户都有开户日期,但你的应用程序不需要知道账户的开户日期,那么你就不需要在你的面向对象设计(bankAccount 类)中添加 开幕日期字段。OOAD 中的 环境保护署Abstraction 与 OOP 中的抽象类无关。

    根据抽象原则,您的实体是现实世界中它们的 抽象。通过这种方式,您可以将银行账户的抽象设计到应用程序所需的细节级别。

  • 继承: 更像是一个编码技巧,而不是一个实际的原则。它使您免于重写在其他地方编写的那些功能。然而,我们的想法是,您正在编写的新代码和您想要重用的旧代码之间必须存在一定的关系。否则,没有人会阻止您编写从 银行户口继承的 动物类,即使它完全没有意义。

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

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

  • 封装: 意味着将相关功能捆绑在一起,并提供对 只有的必要访问。封装是面向对象设计中的 有意义的课堂设计的基础,通过:

    • 整理有关资料和方法;
    • 只公开与外部实体运行相关的数据和方法。

另一个简化的答案是 给你


那些认为 “ OOAD 的抽象导致 OOP 的抽象关键字”..。是错误的人。

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

Abstraction & Encapsulation Example 图像来源

摘要: 显示在猫的左上角和右上角的图像中。外科医生和老太太对动物的设计(或想象)有所不同。同样,您可以根据应用程序的需要在 Cat 类中放置不同的特性。每只猫都有肝脏、膀胱、心脏和肺,但是如果您只需要让您的猫“咕噜”叫,那么您将把应用程序的猫抽象为左上角的设计,而不是右上角的设计。

封装: 由站在桌子上的猫演示。那是 除了猫之外的每个人都应该把猫看作什么。他们不必担心 cat 的实际实现是左上角的还是右上角的,甚至是两者的组合。


另一个详细的答案。

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

抽象: 这有时特指无法实例化的类型,它为其他类型提供了模板,通常通过子类化实现。更一般地说,“抽象”指的是制造/拥有一些不那么详细、不那么具体、不那么颗粒化的东西。

概念之间有一些相似之处和重叠之处,但是记住它的最好方法是这样的: 封装更多的是关于 躲起来的细节,而抽象更多的是关于 一概而论的细节。

抽象和封装是混淆的术语,它们相互依赖。 让我们举个例子:

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;
}
}

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

抽象

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

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

接口提供各方之间的契约,例如,生产者和消费者。生产者生产产品而不让消费者知道产品是如何生产的。但是,通过界面,生产者让所有消费者知道什么产品可以购买。借助于抽象,生产者可以向他们的消费者推销产品。

封装:

封装是抽象的一个层次。同一个产品公司尝试屏蔽彼此生产组的信息。例如,如果一家公司生产葡萄酒和巧克力,封装有助于屏蔽信息,每个产品是如何从彼此。

  1. 如果我有一个单独的包装一个葡萄酒和另一个 如果包中的所有类都声明为 默认访问修饰符,我们给包级封装 所有课程。
  2. 在包中,如果我们将每个类归档(成员字段)声明为 私有的,并且有一个公共方法来访问这些字段 给这些字段提供类级别封装

让我们回到六百万年前,

人类还没有完全进化。首先,进化在身体每个部位旁边创造了一个洞来注入营养,你可以自己决定。

然而,随着人类年龄的增长,每个身体部位的营养需求都会发生变化。人类不知道身体哪个部位需要多少营养。

进化意识到暴露身体每个部位旁边的洞是一个错误,所以它通过 封装中纠正它整个身体的皮肤,只暴露一个开口,后来它被称为“嘴”

此外,营养素按消化系统分配的整个实施过程。你要做的就是继续用嘴吃东西。消化系统会照顾身体营养成分的变化,以满足你的需要。

在软件世界中,需求将不断变化。

封装 内部数据并仅公开所需的函数将有助于更好的维护。因此,与在类/模块/框架内发生的事情相比,您拥有更大的 控制

抽象 使客户端使用类/模块/框架成为 更容易。因此,客户端不必执行(知道)100个不同的步骤来获得所需的输出。公开的函数/类将完成所有工作。在我们的例子中,你不必担心身体的哪个部位需要哪些营养物质。吃吧。