当我们只能使用默认构造函数时,我们是否应该更喜欢组合而不是继承 [英] Should we prefer Composition over Inheritance when we can only use default constructor

查看:59
本文介绍了当我们只能使用默认构造函数时,我们是否应该更喜欢组合而不是继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道组合相对于继承的优势,但在某些情况下,类的实例是由框架使用默认构造函数创建的,我们不能用参数定义构造函数,也不能使用 setter 方法设置对象的属性.为了清楚说明这种情况,请考虑以下示例:

I know the advantages of Composition over Inheritance but in some situation instances of the class are being created by framework using default constructor and we can not define constructor with parameter nor we can set attribute of the object using setter methods. To make this situation clear consider following example:

public class Main {

    public static void main(String... str){
        TargetFramework.component(Child.class);
    }
} 

这里 TargetFramework 获得一个 class,它将使用默认构造函数在幕后创建该类的实例.

Here the TargetFramework get a class and it will create instance of that class behind the scene using default Constructor.

想象一下我想实现 FramewrokInterface 如下:

Imagine I want to implement FramewrokInterface as below:

public interface FrameworkInterface {

    void setup();

    void doAction(Record record);

    void doAnotherAction(Record record, boolean isValid);
}

现在考虑继承和组合,我可以通过两种方式实现这个接口:

Now I can implement this interface in two ways considering Inheritance and Composition:

方法 1:(混合匹配组合和继承)

Approach 1: (Mixing and Matching Composition and Inheritance)

public abstract class Parent implements FrameworkInterface {

    RecordValidator recordValidator;

    @Override
    public abstract void setup();

    @Override
    public void doAction(Record record){
        boolean isValid = recordValidator.validate(record);
        doAnotherAction(record, isValid);
    }
    
    @Override
    public void doAnotherAction(Record record, boolean isValid){

    }
}

在这个实现中,我决定使用组合,并定义了一个 RecordValidator 如下:

In this Implementation I decided to use composition and I've defined a RecordValidator as bellow:

public interface RecordValidator {
    boolean validate(Record record);
}

这里的问题是在创建此类的实例时我无法在 Parent 类中设置 RecordValidator 因为此类的实例是由框架使用默认构造函数创建的,但是我可以在子类的 setup 方法中创建这个实例,它扩展了父类,如下所示:

The problem here is that I can't set RecordValidator in Parent class when creating instance of this class because instances of this class are created by framework using default constructor but I can create this instance in setup method in child Class which extends parent class as below:

public class Child extends Parent {

    @Override
    public void setup() {
        recordValidator = new DefaultRecordValidator();
    }
}

FramworkInterface 的设置方法将在默认构造函数创建实例后立即调用,以便我们可以使用它来初始化我们的 RecordValidator 属性;这对我来说是一种混合和匹配组合和继承,因为我同时使用组合和继承.然而,这种方法有其自身的优势,因为我已经将记录验证的关注点与父类关注点分开了.

The setup method of the FramworkInterface will be called just after instance created by default Constructor so we can use it to initialize our RecordValidator attribute; This is kind of Mixing and Matching Composition and Inheritance together to me because I'm using Composition with Inheritance together. However this approach has its own advantages because I've separated the Concern of validation of record from the Parent class Concerns.

方法 2:(仅继承)

在这种方法中,我通过以下方式实现了 FrameworkInterface:

In this approach I've implemented the FrameworkInterface in the following way:

public abstract class Parent1 implements FrameworkInterface {

    @Override
    public void setup() {

    }

    @Override
    public void doAction(Record record) {
        boolean isValid = validate(record);
        doAnotherAction(record, isValid);
    }

    @Override
    public void doAnotherAction(Record record, boolean isValid) {

    }

    protected abstract boolean validate(Record record);
}

这种方式而不是使用组合和定义 RecordValidator 我已经在我的 Parent1 类中定义了抽象的 validate 方法,以便 Child 类可以使用它来实现验证行为,所以 Child 类可以实现如下:

This way instead of using composition and defining RecordValidator I've defined abstract validate method in my Parent1 class so that Child class can use it to implement validation behaviour, so the Child class can be implemented as follow:

public class Child extends Parent1 {

    @Override
    protected boolean validate(Record record) {
        return false;
    }
}

我的问题是:

哪种方法更适合这种情况,它们的优缺点是什么?

Which approach is better for this situation and what are the pros and cons of them?

推荐答案

哪种方法更适合这种情况,它们的优缺点是什么?

我认为它们都不是最优的,我会寻找其他解决方案.

Which approach is better for this situation and what are the pros and cons of them?

I would argue that both of them are suboptimal to a degree where I would look for other solutions.

查看示例代码,例如,在两种情况下都无法模拟 Child1 的依赖项.您可以通过实现仅用于测试的 setter 或特殊构造函数来引入模拟功能.然而,我在这个设置中遇到的核心问题是你必须向框架低头.

Looking at the sample code, there is, for example, no possibility to mock the dependencies of Child1 in both situations. You could introduce mock capabilities by implementing setters or special constructors that are only used for testing. The core problem I have with this setup, however, is that you bow to the framework.

我建议探索其他可能性,例如手动进行必要的依赖注入,然后注册"带有框架的成品 bean.这就是鲍勃叔叔在谈到 将框架保持在手臂的长度.

I would recommend exploring other possibilities, e.g. do the necessary dependency injection manually, then "register" a finished bean with the framework. This is what Uncle Bob means when he talks about keeping the framework at arm's length.

如果我们开始特别讨论 Java 并且框架不允许任何其他解决方案,例如,预先创建 bean 并将它们注册到框架中,我会联系框架维护者并要求实施 CDI 支持,因为这是处理依赖注入的标准化方法.

If we start talking about Java in particular and the framework does not allow any other solution to, e.g., create beans beforehand and registering them with the framework, I would contact the framework maintainers and ask to implement CDI support since this is a standardized way to handle Depencency Injection.

看看你的例子,你采取了两种不同的方法,即你重新定义了 Parent 的能力.正如您在继承示例中对 Parent 所做的那样,您可以在 Parent 中定义 abstract boolean validate();,将实现委托给 孩子.我什至更进一步定义

Looking at your example, you take two different approaches, i.e. you redefine the capabilites of Parent. Just as you did with Parent in the inheritance example, you could define abstract boolean validate(); in Parent, delegating the implementation to Child. I would even go a step further and define

public interface class Parent extends FrameworkInterface, RecordValidator {
    ...
}

(Parent 中的所有方法要么是抽象的,要么可以视为默认值,该字段可以删除).因此,每个实现这个接口的类都实现了它认为合适的方法.

(all methods in Parent are either abstract or can be seen as defaults, the field can be removed). Thus, each class implementing this interface implements the methods as it sees fit.

这篇关于当我们只能使用默认构造函数时,我们是否应该更喜欢组合而不是继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆