当我们只能使用默认构造函数时,我们是否应该更喜欢组合而不是继承 [英] Should we prefer Composition over Inheritance when we can only use default constructor
问题描述
我知道组合相对于继承的优势,但在某些情况下,类的实例是由框架使用默认构造函数创建的,我们不能用参数定义构造函数,也不能使用 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屋!