Шаблон для списка, который может содержать два типа элементов - PullRequest
0 голосов
/ 30 октября 2018

Я пытаюсь найти хороший способ применить изменение API, которое мы недавно претерпели, которое "ослабляет" тип одного из наших объектов.

Пользователи имели обыкновение предоставлять список ссылок на "Собак". Мы приняли решение, что теперь мы принимаем "Собак" и "Кошек".

Клиентская библиотека использует строго типизированные ссылки, поэтому в настоящее время свойство выглядит следующим образом:

public List<IReference<Dog>> occupants { get; set; }

Я подумал изменить его на:

public List<IReference<Animal>> occupants { get; set; }

Но мне не нравится то, что раньше было проверкой во время компиляции, теперь проверкой во время выполнения (и на стороне сервера). Я бы предпочел иметь что-то более строго набранное, например:

public List<Occupant> occupants { get; set; }

public class Occupant : IReference<Animal> {
    IReference<Animal> _internalReference;
    public Occupant(IReference<Dog> dog) { _internalReference = dog }
    public Occupant(IReference<Cat> cat) { _internalReference = cat }
    //... (Implement IReference<Animal> as a pass-through to _internalReference)
}

К сожалению, поскольку C # не позволяет неявным операторам выполнять преобразование из интерфейса, существующие пользователи, обновляющие свою клиентскую библиотеку, должны будут подвергнуться сотням строк изменения кода, чтобы "обернуть" все свои существующие ссылки Dog в Occupant конструктор.

Итак, затем я подумал о создании нового класса для списка с некоторыми вспомогательными методами:

public class OccupantList : List<Occupant> { 
    public void Add(IReference<Dog> newDog) => base.Add(new Occupant(newDog));
    public void Add(IReference<Cat> newCat) => base.Add(new Occupant(newCat));
    // For backwards compatibility with new list assignments
    public static implicit operator OccupantList(List<IReference<Dog>> legacy) =>
        new OccupantList(legacy.Select(dog => new Occupant(dog)));
}

К сожалению, C # не предоставляет возможности переопределить базовую реализацию ICollection.Add за кулисами, которая используется всеми видами утилит отражения и моим сериализатором JSON для заполнения этого списка, поэтому он жалуется, когда ему дают объекты для добавления, которые отсутствуют. уже не типа Occupant. Я лично столкнулся с проблемами с десериализатором JSON, который я использовал. Я мог бы написать собственный десериализатор для этого класса, но я воспринял это как признак того, что попытка наследовать List<Occupant> и добавить поддержку типов, которые не наследуются от Occupant, была плохой идеей, обреченной на неудачу.

У кого-нибудь есть идеи или примеры, которые могут направить нас в правильном направлении?


Это только "моя проблема"

Настоящим кикером является то, что это не является обратной несовместимостью на уровне API. API использует динамическую типизацию (JSON с интерпретатором Python), поэтому единственное изменение заключается в следующем JSON:

{ "occupants": [{"ref_id":"abc123"}, {"ref_id":"zzz999"}] }

становится:

{ "occupants": [{"ref_id":"abc123", "ref_type": "Dog"}, {"ref_id":"gdr736", "ref_type":"Cat"}] }

С правилами обратной совместимости, которые рассматривают ссылку как "Собаку", если тип не указан.

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

1 Ответ

0 голосов
/ 30 октября 2018

В худшем случае:

Вы будете ненавидеть это, но если у этого API есть какие-то потребители, лучшая практика предполагает, что ваши критические изменения означают, что теперь пришло время для версии вашего API.

В лучшем случае:

Я не вижу упомянутой подписи вашего контроллера API, но если рассматриваемая модель определена как параметр этого метода, вы могли бы быть в состоянии избежать перегрузки метода контроллера, позволяя Связыватель модели обрабатывает другую форму, а затем решает проблему в каком-то промежуточном программном обеспечении или автомате.

Почему бы не решить с изменением модели?

Поскольку, похоже, вы уже выпустили этот API для какого-то внешнего потребления, вы действительно действительно не должны нарушать контракт, описанный этой моделью ввода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...