如何将两个具有共同成员的类组合成一个输入?

时间:2018-05-18 13:53:10

标签: c# .net inheritance composition

我需要创建一个需要3个输入的服务,基本上归结为AB以及我们将调用的AB的组合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; }
}

FeeA中的B是不同且独立的,可能会有所不同,例如CA可以组合使用Bpublic 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,因为从技术上讲,它是服务的输入?

解决这个问题的正确方法是什么?

4 个答案:

答案 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并拥有AFeeBFee

class C: InputBase {
    public decimal AFee { get; set; }
    public decimal BFee { get; set; }
}

然后,您可以为C添加AB的构造函数。这将检查它们是否具有相同的Option1RateDateCreated。如果是,请为C字段指定其值。如果他们不这样做,就抛出异常或类似的东西。

您还可以使用GetAGetB等方法从A的值中创建BC个对象。

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; }
}