Использование интерфейса / Наследования в качестве маркера, когда конкретный тип представляет различные варианты, из которых может быть только один

Довольно полный вопрос, но это принцип ОО, с которым я боролся. Допустим, у меня есть приложение для электронной коммерции и есть концепция способа оплаты, например, CreditCard, Paypal, Apple pay и т. Д. У пользователя есть выбор, какой способ оплаты выбрать, поэтому мне нужно представить их все в списке. на экране и в зависимости от выбора это будет использоваться для управления пользовательским интерфейсом, представляя различные тексты / изображения / взаимодействия, а также будет немного по-разному сериализироваться в запрос платежа по проводной связи.

Вот некоторый код:

public class PaypalPayment : PaymentMethod {

    public string Token;
    public string String;

}

public class CreditCardPayment : PaymentMethod {

    public Address Address;
    public CreditCard CreditCard;

}

interface PaymentMethod {

}


public class Booking {
    public PaymentMethod PaymentMethod; //generic object

    //or

    public PaypalPayment PaypalPayment;
    public CreditCardPayment CreditCardPayment;
}

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

Теоретически я мог бы добавить некоторые методы к интерфейсу PaymentMethod, такие как Serialise() или некоторые методы представления пользовательского интерфейса, которые объединяют все методы оплаты, но тогда мне пришлось бы реализовать их в моем объекте модели, чего я не хочу делать в моей модели слоя.

В целом, у меня нет чистого решения для этого на вашем типичном объектно-ориентированном языке. Я написал это на C#, но это может относиться к любому языку OO.

2 ответа

Данные, кажется, были отделены от логики в вашем дизайне. Как следствие, Booking вероятно, придется потворствовать неуместной близости со своим Payment для того, чтобы поведение имело место, отсюда проблема кастинга.

Идиоматическая реализация ОО 1/ определяет четкую ответственность и 2/ инкапсулирует операции и данные для нее в одном классе. Затем вы можете создать абстракцию поверх семейства этих классов, чтобы их поведение можно было равномерно вызывать из кода потребителя.

Шаблон " Стратегия" или "Политика" может быть хорошим выбором для оплаты.

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

Иметь интерфейс и не получить доступ к базовому типу - это именно то, к чему стремится абстракция и слабая связка. Это не должно быть отстой, но должно быть желательно. Чтобы выбрать все возможные экземпляры, вы можете использовать репозиторий, который возвращает коллекцию всех ваших способов оплаты. Чтобы реализовать этот репозиторий, вы можете использовать ваш любимый ORM и загрузить их из базы данных, использовать ваш любимый контейнер IoC/DI и позволить ему создавать все реализации вашего интерфейса или жестко программировать создание. Все, что подходит для вас, потребностей и потребностей проекта. Если вы также используете интерфейс для хранилища, вы можете поменять местами реализацию.

Другие вопросы по тегам