我需要创建一个需要3个输入的服务,基本上归结为A
,B
以及我们将调用的A
和B
的组合C
。
假设这些类定义如下:
public abstract class InputBase
{
public bool Option1 { get; set; }
public decimal Rate { get; set; }
public DateTime DateCreated { get; set; }
}
public class A : InputBase
{
public decimal Fee { get; set; }
}
public class B : InputBase
{
public decimal Fee { get; set; }
}
Fee
和A
中的B
是不同且独立的,可能会有所不同,例如C
,A
可以组合使用B
和public class C
{
public A A { get; set; }
public B B { get; set; }
}
在同一个请求中。
我们目前只有一个看起来像这样的课程:
var c = new C
{
A = new A(),
B = new B()
}
//this is ugly and what we want to avoid
c.A.DateCreated = DateTime.Now;
c.B.DateCreated = DateTime.Now;
然而,这使得更新两者中常见的任何值都非常烦人。例如:
c.DateCreated = DateTime.Now;
//later
var createdDate = c.A.DateCreated; //points to date created before
我们希望能够做到这样的事情:
C
这样做的天真方式是实现setter,并使用值更新内部类,但接下来我们如何处理getter?
此外,InputBase
是否应继承register
,因为从技术上讲,它是服务的输入?
解决这个问题的正确方法是什么?
答案 0 :(得分:3)
您的继承结构存在某些问题。我不同意其他答案,但我觉得他们已经分心了这些切线元素。
我只想回答你已经回答的直接问题:
这样做的天真方式是实现setter,并使用值更新内部类,但接下来我们如何处理getter?
你对getter问题是正确的,但请记住,不需要存在getter。您可以拥有只写属性。它们很少见,但它们有目的。
我也不确定天真究竟是什么。简单的解决方案也不错。如果有的话,简单的解决方案比复杂的解决方案更好,如果他们设法解决同样的问题。 KISS适用:
KISS原则指出,如果保持简单而不是复杂化,大多数系统都能发挥最佳作用;因此,简单性应该是设计的关键目标,应该避免不必要的复杂性。
请注意强调不必要的复杂性。如果有必要,那么(根据定义)就是有目的的。
var c = new C
{
A = new A(),
B = new B()
}
//this is ugly and what we want to avoid
c.A.DateCreated = DateTime.Now;
c.B.DateCreated = DateTime.Now;
一个简单的解决方法是在C
中创建自定义属性:
public class C
{
public DateTime DateCreated
{
set
{
this.A.DateCreated = value;
this.B.DateCreated = value;
}
}
}
我省略了吸气剂,因为它没有多大意义(你会显示A.CreatedOn
还是B.CreatedOn
)?
但是简单地省略getter并没有错,所以这似乎是最好的方法。
这会创建您想要的行为:
//sets both values
c.DateCreated = DateTime.Now;
但是,值是单独检索的:
var createdDateA = c.A.DateCreated;
var createdDateB = c.B.DateCreated;
这样做的天真方式是实现setter,并使用值更新内部类,但接下来我们如何处理getter?
除了我之前对此的反馈,如果你真的厌恶writeonly属性,你基本上可以将它们转换为方法。在功能上等效,但更明确地不公开get方法:
public class C
{
public void SetDateCreated(DateTime value)
{
this.A.DateCreated = value;
this.B.DateCreated = value;
}
}
另外,C应该从InputBase继承,因为它在技术上是服务的输入吗?
除非C有自己的Fee
对象,与其他派生的InputBase
类完全相同。
这个问题有点宽泛;它取决于代码库中的继承(和SOLID)问题。这对于StackOverflow问题来说太大了。如果你可以将它改为关于继承结构的具体问题,它可能更适合SoftwareEngineering.SE(或者如果你有工作代码,可能更适合CodeReview.SE。)
答案 1 :(得分:0)
你可以让C派生自InputBase,同时拥有一个InputBase集合。
public class C : InputBase, IHaveInputBase
{
IList<InputBase> IHaveInputBase.Inputs { get; set; }
}
public class Service
{
public void Handle(InputBase inputBase)
{
IHaveInputBase haveInputBase = inputBase as IHaveInputBase;
if (haveInputBase != null)
{
foreach (InputBase input in haveInputBase.Inputs)
{
this.Handle(input);
}
}
inputBase.DateCreated = this.dateTimeService.UtcNow();
...
}
}
答案 2 :(得分:0)
我可能没有正确理解您,但为什么不让C
继承InputBase
并拥有AFee
和BFee
:
class C: InputBase {
public decimal AFee { get; set; }
public decimal BFee { get; set; }
}
然后,您可以为C
添加A
和B
的构造函数。这将检查它们是否具有相同的Option1
,Rate
和DateCreated
。如果是,请为C
字段指定其值。如果他们不这样做,就抛出异常或类似的东西。
您还可以使用GetA
或GetB
等方法从A
的值中创建B
和C
个对象。
class C : InputBase {
public decimal AFee { get; set; }
public decimal BFee { get; set; }
public C(A a, B b) {
if (a.DateCreated == b.DateCreated && a.Option1 == b.Option1 && a.Rate == b.Rate) {
Option1 = a.Option1;
Rate = a.Rate;
DateCreated = a.DateCreated;
} else {
throw new ArgumentException("...");
}
}
public C() {}
public A GetA() {
return new A { DateCreated = DateCreated, Option1 = Option1, Rate = Rate, Fee = AFee };
}
public B GetB() {
return new B { DateCreated = DateCreated, Option1 = Option1, Rate = Rate, Fee = BFee };
}
}
答案 3 :(得分:0)
1。您可以使用更新输入方法来执行操作
public class C
{
public A A { get; }
public B B { get; }
public void UpdateInput(Action<InputBase> updateFunc)
{
updateFunc(A);
updateFunc(B);
}
}
然后只需通过Action更新任何InputBase属性。
var c = new C
{
A = new A(),
B = new B()
}
var dateTime = DateTime.UtcNow;
c.UpdateInput(input => input.DateCreated = dateTime);
2。或者您可以将InputBase的属性设为虚拟并按此覆盖它们
public abstract class InputBase
{
public virtual bool Option1 { get; set; }
public virtual decimal Rate { get; set; }
public virtual DateTime DateCreated { get; set; }
}
public class C : InputBase
{
private bool _option1;
public override bool Option1
{
get => _option1;
set
{
_option1 = value;
A.Option1 = value;
B.Option1 = value;
}
}
private decimal _rate;
public override decimal Rate
{
get => _rate;
set
{
_rate = value;
A.Rate = value;
B.Rate = value;
}
}
private DateTime _dateCreated;
public override DateTime DateCreated
{
get => _dateCreated;
set
{
_dateCreated = value;
A.DateCreated = value;
B.DateCreated = value;
}
}
public A A { get; }
public B B { get; }
}