Как реализовать несколько экземпляров интерфейса для разных подклассов? - PullRequest
2 голосов
/ 29 марта 2019

У меня большой класс, который содержал разную информацию для двух разных наборов одного и того же класса.Например, скажем, квитанция, которая может быть квитанцией клиента или внутренней квитанцией.Вся эта информация находится в одном гигантском классе, потому что именно так структурирована база данных, но я хочу разделить классы, чтобы у меня был класс квитанции, содержащий всю общую информацию, класс квитанции клиента и внутренний класс квитанции.Они могут иметь общий интерфейс, но реализации будут другими, и это меня смущает.

Должны ли быть реализованы два отдельных интерфейса?Таким образом, у меня будет 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 ]

1 голос
/ 29 марта 2019

Не запутайтесь, пытаясь заставить две похожие вещи разделить одну абстракцию (базовый класс или интерфейс).Итак, я бы порекомендовал то, что вы предложили: создать два отдельных интерфейса.

Помните, что смысл полиморфизма заключается в том, что вам не нужно знать какой конкретный (производный / реализованный) типНапример, если вы ищете только экземпляр базового типа / интерфейса.Вот и все.Если вам это не нужно, то перепрыгивать через обручи, чтобы заставить два одинаковых класса делить базу, не стоит.

0 голосов
/ 29 марта 2019

Наследование может быть представлено в базе данных по-разному, и есть несколько стратегий в зависимости от ORM, который вы используете. В конце дня, используя одну из стратегий , вы можете основать свой репозиторий на базовом классе и позволить ORM выступать в качестве прокси для разрешения нужного вам экземпляра или попытаться воссоздать себя в уровень хранилища, основанный на поле дискриминатора, необходимые вам экземпляры

Receipt
    ID 
    ReceiptNumber 
    CustomerNumber 
    CustomerEmail 
    CustomerOption 
    InternalNumber 
    InternalEmail 
    InternalOption 
    DISCRIMINATOR_FIELD

(большинство ORM выполняет этот перевод для вас), но для того, чтобы вы поняли идею, вы можете оставить только один репозиторий, чтобы рассматривать все классы как квитанции и сохранять вашу иерархию такой, какая у вас есть.

открытый интерфейс 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

Стратегия наследования в Entity Framework 6

Надеюсь, это поможет

0 голосов
/ 29 марта 2019
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();
    }
}
...