我有一个界面IRecordBuilder
和一个abstract class Query
,其中包含字段protected IRecordBuilder recordBuilder
和方法public abstract IList<IRecords> GetRecordsFromResults();
。
在Query
子类构造函数中,我根据我所在的子类指定了一个recordBuilder具体类型,例如:
recordBuilder = new RecordsPerMonthBuilder(); //RecordsPerMonthBuilder implements IRecordBuilder
我想在上面的抽象方法的实现中使用我的recordBuilder
字段,但是在编译时IRecordBuilder的实现中的属性仍然未知,我无法使用它们。
除了将recordBuilder从母类转移到每个子类并使用正确的类型在那里实例化之外,有没有办法让多态在这里工作?
以下是代码格式的解释:
public interface IRecordBuilder
{
IRecords BuildRecord();
}
public class RecordsPerMonthBuilder : IRecordBuilder
{
public IRecords BuildRecord()
{
if(Foo != null) return new FooRecord(Foo); // class FooRecord : IRecord
return null;
}
public string Foo {get; set;}
}
public abstract class Query
{
protected IRecordBuilder recordBuilder;
public abstract IList<IRecords> GetRecordsFromResults();
}
public sealed class ConcreteQuery: Query
{
public ConcreteQuery()
{
RecordBuilder = new RecordsPerMonthBuilder();
}
public override IList<IRecords> GetRecordsFromResults()
{
var recordsList = new List<IRecords>();
recordBuilder.foo = "foo"; // IRecordBuilder does not contain a definition for foo
recordsList.Add(RecordBuilder.BuildRecord());
return recordsList;
}
}
答案 0 :(得分:0)
我看到三种可能的解决方案:
选项1 :在您的子类中,将构建器强制转换为具体类型(因为子类创建它,它知道具体类型)。如果这样做,您可能希望将recordBuilder
字段只读,并将其传递给基础构造函数,以确保在编译时无法更改其类型。
选项2 :在您的子课程中,请保留一个额外的&#34;强类型&#34;对记录构建器的引用。 (事实上,为什么你甚至需要&#34;界面输入&#34;参考?)
public sealed class ConcreteQuery: Query
{
private RecordsPerMonthBuilder myBuilder;
public ConcreteQuery()
{
myBuilder = new RecordsPerMonthBuilder();
RecordBuilder = myBuilder;
}
public override IList<IRecords> GetRecordsFromResults()
{
var recordsList = new List<IRecords>();
myBuilder.foo = "foo";
recordsList.Add(myBuilder.BuildRecord());
return recordsList;
}
}
选项3 :使您的基类具有通用性:
public abstract class Query<TBuilder> where TBuilder : IRecordBuilder
{
protected TBuilder RecordBuilder;
public abstract IList<IRecords> GetRecordsFromResults();
}
public sealed class ConcreteQuery : Query<RecordsPerMonthBuilder>
{
...
}
答案 1 :(得分:0)
一个令人困惑的地方是,您的Query
课程明确依赖于IRecordBuilder
,RecordsPerMonthBuilder
的一个实施。接口IRecordBuilder
没有Foo
属性,但Query
取决于Foo
属性。 Query
被硬编码为仅使用RecordsPerMonthBuilder
。
很难看到意图。清除它的一种方法是确保在Query
中定义IRecordBuilder
与IRecordBuilder
的实现之间的任何互动。 Query
应该依赖于接口,不应该调用不在该接口中的任何属性或方法。
如果只有IRecordBuilder
的一个实现需要Foo
,则该值不应来自您的Query
类,因为Query
不知道IRecordBuilder
1}}需要Foo
。它不应该知道IRecordBuilder
的实现需要,只知道做什么。
这是一种移动它的方法。你会看到很多这种模式。
public abstract class Query
{
protected IRecordBuilder RecordBuilder { get; private set; }
protected Query(IRecordBuilder recordBuilder)
{
RecordBuilder = recordBuilder;
}
public abstract IList<IRecords> GetRecordsFromResults();
}
现在它永远不会知道IRecordBuilder
的实现是什么。非常好。它现在不可能依赖于IRecordBuilder
界面中没有的任何东西。现在Query
取决于抽象,应用Dependency Inversion原则。
RecordsPerMonthBuilder
怎么样?它取决于值Foo
。 IRecordBuilder
的每个实现都需要吗?如果是这样,您可以将其添加到界面:
IRecords BuildRecord(string foo);
但如果只有一个实现需要该值,则它不应来自Query
,因为Query
不应该知道一个IRecordBuilder
与另一个Foo
之间的差异。我不能更具体地回答这个问题,因为我不知道Query
是什么。
另一个建议:如果ConcreteQuery
和PLANTUML
之间的继承给你带来任何悲伤,那就不要使用继承。有时尝试使用继承会产生并发症,并且不会给我们带来任何好处。