我似乎不太了解两个 OOP 概念。你能解释一下抽象和多态是什么,最好用真实的例子和代码吗?
谢谢你。
想象一个分数类:
class fraction:
int denominator
int numerator
现在有两个对象:
fraction(obj1): denominator=-1 numerator=-1
fraction(obj2): denominator=1 numerator=1
两个对象的值都是 1: (1/1) == (-1)/(-1)
。你不会期望他们的行为与外界有任何不同。那是抽象。您将对象保存的数据抽象为逻辑视图,即使在幕后,还有其他事情。从理论上讲,您有一个等价关系,具有不同的等价组:
[1]=(1, 1), (-1, -1), (5, 5), ...
[2]=(2, 4), (-2, -4), ...
...
并且有一个抽象函数将内部细节抽象到外部:
f((1, 1)) = [1]
f((-1, -1)) = [1]
它从具体值映射到对象的抽象值。例如,您可以通过编写一个构造函数映射 (-1, -1) 到 (1, 1) 并为您的类编写一个 equals 函数来做到这一点。
想象一支笔和两个派生类:
class pen:
void draw(int x, int y)
class pen_thin extends pen:
void draw(int x, int y) { color(x, y) = green; }
class pen_thick extends pen:
void draw(int x, int y) { color(x, y) = green;
color(x, y+1) = green; }
and two objects:
pen_thin(p1)
pen_thick(p2)
两支笔都可以画画。您的一般“笔”无法自行绘制。它只是 pen_thin、pen_thick 和许多其他笔的接口。你说:obj1.draw(1, 0); obj1 是粗笔还是细笔对于您作为用户来说并不重要,对于编译时的编译器也不重要。该调用具有多态性。它是动态多态性(发生在运行时),这就是人们通常的意思。静态多态发生在编译时:
class colorizer:
void colorize(shirt s)
void colorize(pants p)
这叫做重载。你打电话obj.colorize(something)
。如果你用衬衫引用来调用它,它会调用衬衫的版本。如果你用裤子引用来调用它,它将调用裤子版本。这里所做的选择是在 compile-time。
抽象和多态性是关键概念,绝不仅限于 OO。更令人困惑的是,“抽象”这个词有多种使用方式。这是一个带有一个示例的快速备忘单:
数据抽象意味着信息隐藏。通常隐藏的是数据结构的表示。示例:我实现了集合,但我没有告诉您集合是表示为列表、平衡二叉树还是不平衡二叉树。做得对,我可以在不破坏您的代码的情况下更改表示。
多态意味着不同类型的重用。因此,在我的设置示例中,您可以创建一组社会安全号码、一组全名或一组果蝠,所有这些都使用相同的代码。
显然,您可以定义一个既抽象又多态的类。
多态性更加令人困惑,因为有两种方法可以实现多态性。在参数多态中,您可以使用任何类型的值或任何满足某些约束的类型重用该集合。最明显的例子是C++ 模板;如果你写
class Set <T> { ... }
然后T
是集合中包含的对象的类型(符号<T>
表示所谓的“类型参数”,这就是使其成为参数多态性的原因)。
在子类型多态中,您只能对类型是特定类型的子类型的对象重用集合。例如,您可能只能创建提供小于或等于方法的对象集。在像 Smalltalk 或 Ruby 这样真正的面向对象语言中,它们提供所谓的鸭子类型(我们头脑敏锐的理论家有时称之为行为子类型),该方法的存在就足够了。在像 Java 或 C++ 这样将子类型与继承混为一谈的语言中,您对多态性的使用可能仅限于特定类的子类。(Java 通过对类使用一种形式的子类型,在接口上使用另一种形式,进一步混淆了这个问题。)
最后,像我这样的老屁谈论过程抽象,这只是意味着能够将一堆经常一起使用的语句放入一个过程或方法中,然后您可以重用它们。这可能与您的问题无关。
那么,你对困惑感觉更好吗?
这两个是面向对象范式最重要的特征之一。
面向对象将软件建模为真实世界的对象。但是,对 Customer 可能拥有的所有属性或 Employee 拥有的所有属性进行建模会太难(而且没用)。
通过仅列出对象的有趣属性,OO 可以有效地将该对象用于特定域。那是抽象。
例如,人力资源系统中的员工可能具有与在线书店非常不同的属性。我们抽象出的细节是有用的。
在保持相同界面的同时,对象的行为可能会因“类型”而异。
这是什么意思?
例如,在线商店系统可能有两个 Employee 子类
A) 内部员工。
B) 承包商
以及一种计算内部购买折扣的方法
内部员工的折扣计算为:10% + 公司每个工作年的 2% + 每个.. mmhh 孩子的 2%
承包商的折扣是 10%
以下代码用于计算要支付的金额:
public Amount getAmountToPay( Product product, Employee internalCustomer ) {
Amount amount = product.getPrice();
amount.applyDiscount( internalCustomer.getDiscount() );
return amount;
}
会为两种不同的 Employee 产生不同的结果
class Employee {
public int getDiscount();
}
class InternalEmployee extends Employee {
public int getDiscount() {
return 10 + 2 * getWorkedYears() + 2 * getNumberOfChilds();
}
}
class Contractor extends Employee {
public int getDiscount() {
return 10;
}
}
这就是多态性的作用。而不是有类似的东西
Amount amount = product.getPrice();
if( employee.isContractor() ) {
amount.applyDiscount( 10 );
} else if( employee.isSomthingElse() ) {
amount.applyDiscount( 10 * 2 * getYrs() + 2 * getChilds() );
} else if ( employee.contidions, condigions, conditions ) {
amount.applyDiscount( getSomeStrageRuleHere() );
}
我们让运行时选择计算哪一个。就像程序的行为因类型而异:
Amount amount = product.getPrice();
amount.applyDiscount( internalCustomer.getDiscount() );
return amount;
顺便说一句,在这个例子中,“金额”是现实生活概念的抽象,也可以表示为双精度或整数,但也许我们内部有一些有趣的方法,如果设置在它自己的类中会更好。
我希望这有帮助。
抽象是指在不包括背景细节或解释的情况下表示基本特征的行为。类使用抽象的概念,并被定义为抽象属性的列表。
软件抽象的一个例子是 Java 的Object.equals(Object o)
方法。您知道它将将此对象与作为参数传入的对象进行比较,但您不知道,也不需要知道它的具体实现方式(除非您是该类的实现者)。
多态性意味着能够采用一种以上的形式。一个方法在不同的实例中可能有不同的行为。行为取决于操作中使用的数据类型。
多态性的经典示例之一使用植根于 Animal 类的继承树。所有 Animal 都有一个makeNoise()
方法,但是 Dog 类和 Cat 类实现它的方式不同。这允许您使用 Animal 引用类型来引用任何 Dog's 和 Cat's。
Animal a = new Dog();
Animal b = new Cat();
现在您可以调用makeNoise()
任一 Animal 实例并知道它会发出适当的噪音。如果您有一个动物集合,并且您在运行时不知道每个动物到底是什么类型,这将特别有用。
这两个术语在面向对象编程中大量使用,但它们并不特别限于该上下文。
抽象是对其他事物的概括;视野更上一层楼。例如,层次结构可以看作是对公司组织结构的抽象。通常,它用于底层事物(例如它们的基本类型)的上下文中。抽象的目的是编写更少的代码,而这些代码本质上更通用,这样您就可以运行它来解决更大的问题集。例如,电子表格是一种抽象,它允许特定类型的信息存储。更多的?
多态性也是一种概括,但它发生在运行时上下文中。如果有某种方法可以在它们彼此无法区分的情况下访问它们,那么一堆不同的对象类型是多态的。也就是说,所有对象的外观和感觉都相同,即使它们不同。这样做的目的是显着减少代码;您可以编写一个通用的解决方案来避免为每种不同类型编写所有不同的排列。如果你写一个图形库,你宁愿只写一些抽象代码来处理“形状”,然后必须为每种不同的类型编写代码,比如圆形、正方形等。
这两个术语都以代码中的属性为中心,使程序员能够事半功倍。更少的代码有更少的错误,更稳定,更容易维护。另一种方法是使用“蛮力”来破解数百万行非常具体(并且非常脆弱)的代码。更多的代码更难修复,更难保持最新。
保罗。
简短的回答:抽象是概念性的,多态性是行为性的
在面向对象的上下文中,关于抽象的实际含义的混淆是可以理解的:它对继承、封装甚至多态的概念几乎没有增加什么。如果掌握了这三个概念,一旦“抽象”自然嵌入其中(尤其是继承),就无需过多关注。
首先,请注意术语“抽象”有多种含义,并且声明并不正确,例如,封装需要抽象:当您使用访问修饰符来保护类的属性同时公开处理它们的方法时(即封装是什么),类的用户不再需要担心如何自己处理它们。所以,从某种意义上说,当你设计一个类时,你通过适当地封装方法和属性进行抽象——类的用户需要做的就是通过调用正确的方法来使用它,这是一种抽象形式。
此外,如果您直截了当地思考,多态性也是一种抽象形式:您的代码调用某个类提供的方法,并且在确定实际类类型(在运行时)之前,您不知道它将如何作用。所以,说多态行为是一种抽象是正确的。
然而,当作为一个独立的术语来描述 OOP 的特性时,抽象必须被理解为以合适的类层次结构的形式对正在讨论的系统进行适当的表示。因此,抽象是设计者的心理过程的结果,最终为将在程序中使用的类进行适当的设计。引用可以在 javarevisited 博客上找到的(优秀的!)帖子:
...抽象在设计级别隐藏细节,而封装在实现级别隐藏细节。
虽然上面的陈述是正确的,但我发现“隐藏细节”部分被错误地陈述了——我会将它改写为
抽象关注设计细节,决定类层次结构的外观,封装隐藏细节实现。
公平地说,这个想法在他的文章中很漂亮。具有这种含义的“抽象”一词在Head First Object-Oriented Analysis and Design等好书中也可以看到,我从那里引用了一个声明:
每当您在两个或更多地方发现共同行为时,请将该行为抽象为一个类,然后在公共类中重用该行为
请注意此处抽象的用法:“将行为抽象为一个类”。现在,如果抽象意味着如上所述正确设计类层次结构,则可以利用继承和封装的概念,方便地使用类将抽象定义为域的表示。
在 Java 的特殊情况下,抽象是通过使用接口和抽象类来实现的,而封装是通过private、protected 和 package 访问修饰符来实现的。
简单来说,抽象是概念性的,Poly 是行为性的。为了在 OOP 中实现抽象,您需要 Poly。
面向对象编程中的抽象我可以说是一种概念或设计模式,它可以实现更好的隔离、松散耦合、可测试性、可重用性和可扩展性。为了实现所有,我们需要poly、继承/扩展等。
抽象和多态在本质上是相似的,但目的不同。
例如。
驾驶执照:您会获得一张执照,上面注明您可以驾驶的车辆类别。许可证提到了当局允许的车辆类别,但没有定义或提及您应该驾驶的特定汽车或品牌。这是抽象。
这里的许可证是一个抽象类及其方法,允许的车辆是它的抽象方法。
现在,在这里,多态性是当局将个人许可证分配给不同的人的不同方式,根据不同的要求,有些是为轻型车辆颁发的,有些是为重型车辆颁发的,有些是为商用车颁发的。在这里,License 是一个基类,其他种类的license 是它的子类,也遵循is-a 关系。商业许可证是许可证。
因此,抽象是一种通用准则,它赋予后续类实现独立性,而多态是差异化方法,它覆盖父类设置的方法/规则。
PS:最近开始学习java答案是基于我的观察,如果我错了请纠正我。
抽象和多态基本上在编程中做几乎相同的工作。
让我们以汽车为例..
无论是福特小型货车、法拉利异国情调、路虎 SUV 还是宝马轿车,它们都遵循汽车的基本设计,即发动机、方向盘、变速箱、灯、指示灯和名单还在继续。使它们不同的是它们的具体实现,例如法拉利可能具有比小型货车更强大的发动机,suv 可能具有不同的变速箱,因此汽车(此处为超类)已由子类(轿车,suv,小型货车)实现, 异国情调)这是多态性,通过添加其他规范来继承或实现的基本思想。以各种形式(子类)实施的四轮车辆(超类)
现在,抽象,根据定义,它意味着隐藏细节并让用户看到他需要的东西。
让我们再次以汽车为例..您使用齿轮,但您不确切知道齿轮的工作原理以及改变速度的机制等等。
现在转到编码部分。
抽象类是不完整的类,顾名思义,抽象类需要有一个不完整的方法,该方法需要由继承超类的子类完成,如果它们不完成抽象方法,它们也将保持不完整.
abstract class car {
abstract void gear();
}
class sedan extends car {
public void gear()
{
//complete the method
}
}
你也不能创建抽象类的对象,因为类不完整。然而,这些抽象类可以具有静态方法、参数、具体方法,但要使其成为抽象类,它们需要一个抽象方法。因此,一个基本的抽象超类在其他子类中实现,它们在其中完成它。通过查看方法声明,我们可以估计该方法正在做什么,它将返回什么。但是我们不知道抽象方法将如何实现。
通过使用抽象类或接口,我们可以在 Java 中实现抽象。众所周知,抽象类、接口包含抽象方法
我们只能估计它们将如何工作。一旦我们在实现相应抽象类或接口的类中提供了方法实现,我们就会了解它们是如何工作的。
因此,抽象基本上有助于多态性。
很容易。
抽象就是抽象。“学生”类是对真实学生的抽象。
多态性是当一个类代表另一个类时,用户不会注意到。当类实现相同的接口或一个类从另一个类派生时,可能会发生这种情况。“HighSchoolStudent”类派生自“Student”类。当“教师”类在对象上调用#attendance 方法时,它可能不知道该对象是“学生”类还是“HighSchoolStudent”类。