[英]Java - Force subclasses to call super method after constructor
我希望一堆子類在完成如下構造函數后調用一個超級方法:
public abstract class Superclass {
...
public Superclass(...) {
... // do stuff before initializing subclass
}
protected void dispatch() { //method to be called directly after creating an object
doStuff();
...
}
public abstract void doStuff();
}
public class Subclass extends Superclass {
...
public Subclass(...) {
super(...); //has to be the first line
... //assign variables etc.
dispatch(); //has to be called after variables are assigned etc.
}
public void doStuff() {
//do stuff with assigned variables etc.
}
}
dispatch()
函數包含對象創建后的一系列操作,該對象必須應用於所有子類。 我不能將此函數移到超級構造函數中,因為它從需要已分配變量的子類中調用方法。 但是由於super()
必須是子構造函數的第一行,因此在調用super構造函數之前,我無法設置任何變量。
它現在可以正常工作,但是我發現在每個子類的構造函數的末尾調用dispatch()
是一個不好的概念。 有沒有更優雅的方式來解決這個問題? 還是我應該完全重新考慮我的概念?
您的請求違反了幾種Java最佳實踐,例如:
不要在構造函數中進行復雜的配置,僅填充私有(最終)成員變量,並且僅執行非常基本的一致性檢查(如果有的話)。
不要從構造函數中調用non private
non final
方法,甚至不能間接調用。
因此,我強烈建議您考慮一下類的設計。 很有可能您的班級太多且有太多的責任感。
它現在可以正常工作,但是我發現在每個子類的構造函數的末尾調用dispatch()是一個不好的概念。 有沒有更優雅的方式來解決這個問題? 還是我應該完全重新考慮我的概念?
正如Timothy Truckle所強調的那樣,您的構造函數邏輯過於復雜。
通過使用模板方法初始化子類實例,可以使事情變得更簡單並達到目標。 請注意,您已經在doStuff()
使用了此模式。
子類構造函數的確是您遇到的問題:您希望減少每個子類中所需的強制性樣板,並使它們的可讀性和維護性更好。
因此,在超類中引入一個新的模板方法,並從超類的構造函數中調用它。
此方法與構造函數的作用相同,但可以通過更靈活的方式調用。
也是一種人工方法的dispatch()
,也不需要僅為該技巧引入。
整個邏輯可以從超類構造函數進行編排。
超級類可能看起來像:
public abstract class Superclass {
...
public Superclass(...) {
... // do stuff before initializing subclass
init();
doStuff();
}
public abstract void init();
public abstract void doStuff();
}
並在子類中,替換為:
public Subclass(...) {
super(...); //has to be the first line
... //assign variables etc.
dispatch(); //has to be called after variables are assigned etc.
}
創建人:
public Subclass(...) {
super(...); // let the super constructor to orchestrate the init logic
}
public void init(){
// move the constructor logic here
}
結果要簡單得多,因為此設計可以在一個地方(即超類構造函數)集中收集與子類的初始化“算法”相關的職責。
關於您的評論:
這確實比我做的還要優雅。 謝謝! 編輯:剛注意到,這不適用於具有不同構造函數參數的子類。 任何想法如何解決這個問題?
出於這樣的要求,為了使事情變得簡單明了,您必須分兩步進行:
init()
方法。 它可能看起來像:
SuperClass o = new Subclass(argFoo, argBar);
o.init();
這種方法的問題在於您不確定是否調用了init()
方法。 您可以添加一個標志,每次在對象上調用方法時都要檢查該標志。 但這確實很麻煩且容易出錯。 避免那樣。
為了改善這一點,我可能會使用包裝模式。
您也可以使用攔截器/方面。 但這不是一個好用例:init處理不是橫向的,實際上與對象行為有關。 使其可見更有意義。
使用包裝器,它看起來可能像:
SuperClass o = new MyWrapper(new Subclass(argFoo, argBar));
其中MyWrapper
是的子類, SuperClass
和包裝的一個實例SuperClass
對象:
public class MyWrapper implements SuperClass{
private SuperClass wrapped;
public MyWrapper (SuperClass wrapped){
this.wrapped = wrapped;
this.wrapped.init();
}
// then delegate each superclass method to the wrapped object
public void doStuff(){
this.wrapped.doStuff();
}
// and so for...
}
如果您確實需要在調用該方法時完成任何子類實例化,那么Lorelorelore是正確的。 否則,您可以做您所擁有的。 如果其他人需要使用該代碼,我建議提出足夠的注釋。
您可以通過提供一個static
方法來抽象您對SuperClass的使用,該方法將執行您想要的代碼,但還可以檢查它是否已正確設置:
public abstract class SuperClass{
private boolean instantiated;
public SuperClass(...){
...
}
public abstract void doStuff();
private void dispatch(){
if(!instantiated){
instantiated = true;
doStuff();
}
}
public static void executeActionOnSuperClass(SuperClass s){
s.dispatch(); // call instantiation if not already done
s.executeAnAction();
}
}
和子類:
public class SubClass extends SuperClass{
public SubClass(...){
super(...);
}
public void doStuff(){
...
}
}
然后可以這樣執行:
SuperClass.executeAnActionOnSuperClass(new SubClass(...));
雖然這主要是一種反模式,但應該備用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.