pol*_*nts 292
它们完全不同.继承是一种"是一种"关系.作文是"有一个".
您可以通过将另一个类的实例作为类C的字段来进行组合,而不是扩展C.一个很好的例子,其中组合将比继承更好java.util.Stack,目前正在扩展java.util.Vector.现在这被认为是一个大错.堆栈"is-not-a"向量; 你不应该被允许任意插入和删除元素.它应该是组成而不是.
不幸的是,现在纠正这个设计错误为时已晚,因为现在更改继承层次结构会破坏与现有代码的兼容性.如果Stack使用组合而不是继承,可以始终修改它以使用其他数据结构而不违反API.
我强烈推荐Josh Bloch的书Effective Java 2nd Edition
良好的面向对象设计不是关于自由扩展现有类.你的第一直觉应该是构图.
也可以看看:
cod*_*ict 196
组合意味着HAS A
继承意味着IS A
Example:汽车有发动机,汽车是汽车
在编程中,这表示为:
class Engine {} // The Engine class.
class Automobile {} // Automobile class which is parent to Car class.
class Car extends Automobile { // Car is an Automobile, so Car class extends Automobile class.
private Engine engine; // Car has an Engine so, Car class has an instance of Engine class as its member.
}
Run Code Online (Sandbox Code Playgroud)
小智 39
继承如何危险?
让我们举个例子
public class X{
public void do(){
}
}
Public Class Y extends X{
public void work(){
do();
}
}
Run Code Online (Sandbox Code Playgroud)
1)如上面的代码所示,Y类与X类有很强的耦合.如果超类X有任何变化,Y可能会突然发生.假设在将来的类X中实现具有以下签名的方法工作
public int work(){
}
Run Code Online (Sandbox Code Playgroud)
更改在X类中完成,但它将使Y类无法编译.因此,这种依赖可以达到任何级别,这可能非常危险.每当超类可能无法完全看到其所有子类中的代码时,子类可能会一直注意到超类中发生的事情.所以我们需要避免这种强烈和不必要的耦合.
作文如何解决这个问题?
让我们看看修改相同的例子
public class X{
public void do(){
}
}
Public Class Y{
X x = new X();
public void work(){
x.do();
}
}
Run Code Online (Sandbox Code Playgroud)
这里我们在Y类中创建X类的引用,并通过创建X类的实例来调用X类的方法.现在所有强大的耦合都消失了.超类和子类现在彼此高度独立.类可以自由地进行在继承情况下危险的更改.
2)组合的第二个非常好的优点在于它提供了调用灵活性的方法,例如:
class X implements R
{}
class Y implements R
{}
public class Test{
R r;
}
Run Code Online (Sandbox Code Playgroud)
在使用r引用的Test类中,我可以调用X类和Y类的方法.这种灵活性从来没有继承过
3)另一个很大的优势:单元测试
public class X {
public void do(){
}
}
Public Class Y {
X x = new X();
public void work(){
x.do();
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,如果x实例的状态未知,则可以使用一些测试数据轻松地对其进行模拟,并且可以轻松地测试所有方法.这在继承中根本不可能,因为您严重依赖于超类来获取实例的状态并执行任何方法.
4)我们应该避免继承的另一个好理由是Java不支持多重继承.
让我们举个例子来理解这个:
Public class Transaction {
Banking b;
public static void main(String a[])
{
b = new Deposit();
if(b.deposit()){
b = new Credit();
c.credit();
}
}
}
Run Code Online (Sandbox Code Playgroud)
很高兴知道 :
在运行时很容易实现组合,而继承在编译时提供它的功能
组合也被称为HAS-A关系,继承也称为IS-A关系
因此,出于上述各种原因,习惯于总是更喜欢构图而不是继承.
小智 19
@Michael Rodrigues给出的答案是不正确的(我道歉;我无法直接评论),并可能导致一些混乱.
接口实现是一种继承形式 ......当您实现接口时,您不仅要继承所有常量,而且要将对象提交为接口指定的类型; 它仍然是一种" 是一种 "关系.如果汽车实现Fillable,那么汽车" 是一个 " 可填充的,并且可以在您使用Fillable的任何地方使用.
组成与继承根本不同.当您使用合成时,您(在其他答案中注释)在两个对象之间建立" has-a "关系,而不是在使用继承时创建的" is-a "关系.
所以,从其他问题的汽车示例中,如果我想说一辆汽车" 有一个 "油箱,我会使用组成,如下:
public class Car {
private GasTank myCarsGasTank;
}
Run Code Online (Sandbox Code Playgroud)
希望这可以解决任何误解.
fri*_*ley 17
继承带来了IS-A关系.构图带来了HAS-A关系.策略模式解释组合应该用于存在定义特定行为的算法族的情况.
经典的例子是鸭子类,它实现了飞行行为.
public interface Flyable{
public void fly();
}
public class Duck {
Flyable fly;
public Duck(){
fly = new BackwardFlying();
}
}
Run Code Online (Sandbox Code Playgroud)
因此,我们可以有多个实现飞行的类,例如:
public class BackwardFlying implements Flyable{
public void fly(){
Systemout.println("Flies backward ");
}
}
public class FastFlying implements Flyable{
public void fly(){
Systemout.println("Flies 100 miles/sec");
}
}
Run Code Online (Sandbox Code Playgroud)
如果是继承,我们会有两种不同类型的鸟类一遍又一遍地实现飞行功能.所以继承和构成完全不同.
组合就像听起来一样 - 通过插入部件来创建对象.
编辑其余答案错误地基于以下前提.
这是通过Interfaces完成的.
例如,使用Car上面的例子,
Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel
Run Code Online (Sandbox Code Playgroud)
因此,使用一些标准的理论组件,您可以构建您的对象.然后,您的工作就是填写如何House保护其居住者,以及如何Car保护其居住者.
继承就像反过来一样.您从一个完整的(或半完成的)对象开始,然后替换或覆盖您想要更改的各个位.
例如,MotorVehicle可以带有Fuelable方法和Drive方法.您可以保留Fuel方法,因为填充摩托车和汽车是一样的,但是您可以覆盖该Drive方法,因为摩托车驾驶方式与a非常不同Car.
通过继承,一些类已经完全实现,而其他类则有被强制覆盖的方法.随着作文没有给你任何东西.(但是你可以通过调用其他类中的方法来实现接口,如果碰巧有东西存在的话).
组合被认为更灵活,因为如果你有一个像iUsesFuel这样的方法,你可以在其他地方(另一个类,另一个项目)有一个方法,只需要担心处理可以加油的物体,无论它是否是汽车,接口要求说他们实现该接口的类实际上具有该接口所有的方法.例如,
iFuelable Interface:
void AddSomeFuel()
void UseSomeFuel()
int percentageFull()
Run Code Online (Sandbox Code Playgroud)
那么你可以在其他地方找到一个方法
private void FillHerUp(iFuelable : objectToFill) {
Do while (objectToFill.percentageFull() <= 100) {
objectToFill.AddSomeFuel();
}
Run Code Online (Sandbox Code Playgroud)
奇怪的例子,但它表明这个方法并不关心它填满了什么,因为对象实现了iUsesFuel,它可以被填充.故事结局.
如果您使用的继承,而不是,你需要不同的FillHerUp方法来处理MotorVehicles和Barbecues,除非你有一些怪异,而"ObjectThatUsesFuel"的基地,以继承对象.
组成和继承是一样的吗?
它们不一样.
组合:它使一组对象必须以与单个对象实例相同的方式处理.复合的意图是将对象"组合"成树结构以表示部分整体层次结构
继承:类从其所有超类继承字段和方法,无论是直接还是间接.子类可以覆盖它继承的方法,也可以隐藏它继承的字段或方法.
如果我想实现组合模式,我该如何在Java中实现?
维基百科文章足以在java中实现复合模式.
主要参与者:
组件:
叶:
复合:
用于理解Composite模式的代码示例:
import java.util.List;
import java.util.ArrayList;
interface Part{
public double getPrice();
public String getName();
}
class Engine implements Part{
String name;
double price;
public Engine(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Trunk implements Part{
String name;
double price;
public Trunk(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Body implements Part{
String name;
double price;
public Body(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Car implements Part{
List<Part> parts;
String name;
public Car(String name){
this.name = name;
parts = new ArrayList<Part>();
}
public void addPart(Part part){
parts.add(part);
}
public String getName(){
return name;
}
public String getPartNames(){
StringBuilder sb = new StringBuilder();
for ( Part part: parts){
sb.append(part.getName()).append(" ");
}
return sb.toString();
}
public double getPrice(){
double price = 0;
for ( Part part: parts){
price += part.getPrice();
}
return price;
}
}
public class CompositeDemo{
public static void main(String args[]){
Part engine = new Engine("DiselEngine",15000);
Part trunk = new Trunk("Trunk",10000);
Part body = new Body("Body",12000);
Car car = new Car("Innova");
car.addPart(engine);
car.addPart(trunk);
car.addPart(body);
double price = car.getPrice();
System.out.println("Car name:"+car.getName());
System.out.println("Car parts:"+car.getPartNames());
System.out.println("Car price:"+car.getPrice());
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
Car name:Innova
Car parts:DiselEngine Trunk Body
Car price:37000.0
Run Code Online (Sandbox Code Playgroud)
说明:
有关构成和继承的优缺点,请参阅下面的问题.
| 归档时间: |
|
| 查看次数: |
205998 次 |
| 最近记录: |