如何为不同的子类实现接口的多个实例?

时间:2019-03-29 12:35:38

标签: c# asp.net-core .net-core solid-principles

我有一个大类,用于为同一类的两个不同集合保存不同的信息。例如,说一个收据,它可以是客户收据或内部收据。所有这些信息都在一个巨型类中,因为这是数据库的结构方式,但是我想将这些类分开,这样我就可以拥有一个容纳所有共享信息的收据类,一个客​​户收据类和一个内部收据类。他们可以共享一个接口,但是实现方式会有所不同,这让我感到困惑。

是否应实现两个单独的接口?所以我会有一个ICustomerReceipt和IInternalReceipt吗?我认为应该有一个带有Get()和Save()方法的接口,并且基于实现(如果是客户或内部收据),我可以获得该类的特定信息。我在这里迷路了。

public class Receipt { 
    public int ID { get; set; } 
    public int ReceiptNumber { get; set; }
    public List<string> Items { get; set; }
}

public class CustomerReceipt : Receipt { 
    public string CustomerNumber { get; set; } 
    public string CustomerEmail { get; set; }
    public string CustomerOption { get; set; }
}

public class InternalReceipt : Receipt {
    public string InternalNumber { get; set; }
    public string InternalEmail { get; set; }
    public string InternalOption { get; set; }
}

public interface IReceiptRepository { 
    public Receipt Get(int id);
    public Receipt Add(Receipt receipt);
}

public CustomerReceiptRepository : IReceiptRepository {
    public CustomerReceipt Get(int id) {
        // get information about customer receipts here
    }
}

public InternalReceiptRepository: IReceiptRepository {
    public InternalReceipt Get(int id) {
        // get information about internal receipts here
    }
}

基本上,我只想将正确的回执返回到控制器中具有通用“ ReceiptNumber”或“ ReceiptEmail”的视图模型。我知道这不是最好的例子,但这是我唯一能想到的例子。

3 个答案:

答案 0 :(得分:1)

不要试图强迫两个相似的东西共享一个抽象(基类或接口)。因此,我建议您提出以下建议:创建两个单独的接口。

请记住,多态性的重点是,如果您仅查找基本类型的实例,则不必知道实例的具体类型(派生/实现)。接口。而已。如果您不需要这样做,那么跳过箍以迫使两个相似的类共享一个基数是不值得的。

答案 1 :(得分:0)

public interface IReceiptRepository { 
    public Receipt Get(int id);
    public Receipt Add(Receipt receipt);
}

public CustomerReceiptRepository : IReceiptRepository {
    public Receipt Get(int id) {
        // get information about customer receipts here
        return new CustomerReceipt();
    }
}

public InternalReceiptRepository: IReceiptRepository {
    public Receipt Get(int id) {
        // get information about internal receipts here
        return new InternalReceipt();
    }
}

答案 2 :(得分:0)

继承可以用不同的方式表示在数据库上,并且根据您使用的ORM有一些策略。 归根结底,您可以使用其中一种策略,将存储库基于基类,并让ORM充当代理来解析所需的实例,或者尝试重新创建自己,在存储库级别,根据鉴别字段,您需要的实例

Receipt
    ID 
    ReceiptNumber 
    CustomerNumber 
    CustomerEmail 
    CustomerOption 
    InternalNumber 
    InternalEmail 
    InternalOption 
    DISCRIMINATOR_FIELD

(大多数ORM都会为您完成此翻译),但是为了使您理解,您只能保留一个存储库以将所有类都视为Receipt,并保持层次结构不变。

公共接口IReceiptRepository {     公共收据Get(int id);     公开收据添加(收据); }

public CustomerReceiptRepository : IReceiptRepository {
    public Receipt Get(int id) {
            var rec = DbContext.Table.Receipt.FirstOrDefault(r => r.id = id);
            if(rec.DiscriminatorField == 1) //CustomerReceipt
            {
                return new CustomerReceipt
                {
                     ID = ...
                     ReceiptNumber = ...
                     CustomerNumber = ...
                     CustomerEmail = ...
                     CustomerOption = ...
                 }
              }
              //all other cases are InternalReceipts
              return new InternalReceipt
              {
                   ID = ...
                   ReceiptNumber = ...
                   InternalNumber  = ...
                   InternalEmail = ... 
                   InternalOption = ...
              }
    }
}

与Add方法相同,只填充该对象所需的字段。这种构成将一切都基于鉴别字段。 我不建议您 以这种方式实施解决方案,但是这样,您仍然可以在ViewModel上获得通用收据。我的建议是,您了解有关正在使用的ORM的更多信息,以及在那里如何表示继承(也许您是先使用数据库而不是代码,并且您将需要手动处理这些事情,因为数据库不是以这种方式设计的并且您需要采取与我建议的方法类似的方法。但是,如果您有机会创建POCO类并创建数据库,那么绝对值得一看的是它们如何实现继承。

在这里,我附上了如何在EntityFramework 6上解决此问题的链接

Inheritance Strategy in Entity Framework 6

希望这会有所帮助