[英]Difference between abstract class whose constructor requires arguments, and abstract class with abstract get-only properties
public abstract class BaseProcessor
{
public abstract void Initialize();
private readonly string _executerPluginName;
private readonly ILogService _logService;
public BaseProcessor(string executerPluginName, ILogService logService)
{
this._executerPluginName = executerPluginName;
this._logService = logService;
}
protected void CallExecutor()
{
this.Initialize();
//common logic
}
}
public class ConcreteProcessor : BaseProcessor
{
public override void Initialize()
{
//concrete logic
}
public ConcreteProcessor(string executerPluginName, ILogService logService) : base(executerPluginName, logService)
{
}
}
和
public abstract class BaseProcessor
{
public abstract void Initialize();
protected abstract string ExecuterPluginName { get; }
protected abstract ILogService LogService { get; }
protected void CallExecutor()
{
this.Initialize();
//common logic
}
}
public class ConcreteProcessor : BaseProcessor
{
protected override string ExecuterPluginName { get{ throw new NotImplementedException(); } }
protected override ILogService LogService { get{ throw new NotImplementedException(); } }
public override void Initialize()
{
//concrete logic
}
}
什么樣的繼承更可取? 我將使用第二種解決方案,但是我對抽象的數量有些懷疑。 您可以對這些方法進行一些解釋嗎?
構造函數參數始終成為代碼的必填字段。 使其具有屬性,使其成為可選屬性,並且可能使客戶端無法使用。 如果不使用它們,則可能會導致運行時錯誤。
如果您要使用第二種方法,則我希望使用接口而不是抽象類。
區別在於,在第一個示例(基類構造函數需要參數)中,必須在實例完全構造之前(由繼承者)指定值。
在第二個例子中(基類有abstract
get
-only屬性)它是具體的實施提供返回值的更復雜的“計算”些,他們可能會認為目前的實例已經正確構造(走的優勢)。
我不確定您使用列出的方法時所遇到問題的背景如何,因此我的回應有些刺痛。 少不了,這是我的2美分。
第一種方法允許您在基類的方法中使用ILogService
和ExecuterPluginName
。 尤其對於諸如日志記錄之類的常見活動,這可以派上用場。
僅出於這個原因,我更喜歡這種方法而不是第二種方法。
但是,如果您沒有任何使用共享資源的通用邏輯,則將其保留在基類中幾乎沒有任何意義。 YAGNI在這里適用。 實際上,我可能會說得更遠,如果沒有通用邏輯,則基本抽象類是沒有意義的。
如果您只是想強迫具體的類在其實現中遵守方法和屬性的約定,那么使用接口可能會很麻煩。
此外,使用throw new NotImplementedException();
絕對是代碼氣味。
旁注:您可以利用依賴項注入(通過DI框架,例如Autofac等)來控制傳遞到具體類中的對象的生存期。 也就是說,您可以使您的日志記錄服務成為單例,然后將其相同實例傳遞給所有實現。
考慮“二維矩陣”抽象類的以下三種實現:
abstract public class Matrix2dv1
{
double[,] data;
protected Matrix2dv1(double[,] source)
{
data = (double[,])source.Clone();
}
public double this[int row,int column]
{
get { return data[row, column]; }
}
}
abstract public class Matrix2dv2
{
abstract public double this[int row, int column] { get; }
}
abstract public class Matrix2dv3
{
public double this[int row, int column]
{
get { return getRowColumn(row,column); }
}
protected abstract double getRowColumn(int row, int column);
}
該類的第一個版本代表派生類處理更多的工作,但是它要求每個實現都使用double[,]
作為后備存儲。 如果索引的getter是虛擬的,則派生類可以將虛擬數組(甚至是null
)傳遞給構造函數並使用其自己的后備存儲,但是在許多情況下,它們仍然最終會浪費掉至少用於存儲該數組的存儲。基類data
字段。
該類的第二個版本將要求客戶端做一些工作,但是會允許諸如IdentityMatrix
類的類型可能不需要數組作為后備存儲(它可以簡單地定義其索引的getter以row
和column
時返回1.0的可能性)。相等,否則為0.0)。 不幸的是,如果基類需要同時支持可變和不可變的子類型,那將不能很好地工作,因為派生類無法同時覆蓋基類的屬性getter和定義讀寫屬性。
該類的第三個版本通過具有一個具體的非虛擬屬性獲取器避免了第二個版本的問題,該獲取器除了鏈接到虛擬方法外不執行任何操作。 派生類在覆蓋該方法和定義new
非虛擬讀寫屬性方面都沒有問題,該屬性的getter鏈接至同一方法,setter鏈接至不同的虛擬方法。
是否應在基類中實現屬性的問題通常取決於是否有現實的可能性,即某些派生類可能希望擁有不同於大多數典型形式的后備存儲形式,因此可能沒有用於最常見類型的后備存儲字段。 請注意,如果90%的派生類將從一個字段和代碼中受益,而其中一些則沒有,那么從基類派生一個“中間級”抽象類(例如ArrayBackedMatrix
)可能會有所幫助但包括通用字段。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.