Является ли абстрагирование типа данных (иногда) хорошей идеей? - PullRequest
2 голосов
/ 06 октября 2008

Существует множество случаев, когда у вас есть интерфейс, который принимает аргументы аналогичного типа, которые имеют отдельный логический смысл домена:

public static class Database
{
   public static bool HasAccess(string userId, string documentId) { return true; }
}

Теперь довольно просто иметь какой-либо ключевой documentId вместо userId и наоборот. Это можно предотвратить, абстрагировав тип данных аргументов:

public class UserId
{
   public string Value { get; internal set; }
   public static implicit operator string(UserId id) { return id.Value; }
}

public class DocumentId
{
   public string Value { get; internal set; }
   public static implicit operator string(DocumentId id) { return id.Value; }
}

public static class Database
{
    public static bool HasAccess(UserId userId, DocumentId documentId) { return true; }
}

Таким образом, вы получаете хорошее предупреждение компилятора, если вводите аргументы не по порядку:

UserId e = new UserId() { Value = "a" };
DocumentId d = new DocumentId() { Value = "b" };

Database.HasAccess(d, e);

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

Вопрос относится к решениям на C #, но приветствуются короткие описания на других языках.

РЕДАКТИРОВАТЬ: Убрал неявное приведение из строки и указывая пальцем на тег C #.

Ответы [ 9 ]

2 голосов
/ 06 октября 2008

Я думаю, что вы ответили на свой вопрос - лучше целостность данных и проверка, лучшая система

1 голос
/ 06 октября 2008

В данном случае это не выглядит мне ценным.

Вы добавили 12 строк, распределенных по двум дополнительным классам. В некоторых языках вам нужно управлять двумя новыми файлами для этого. (Не уверен в C #). Вы ввели много дополнительной когнитивной нагрузки. Эти классы появляются всякий раз, когда вы перемещаетесь по списку классов; они появляются в вашей автоматически сгенерированной документации; они находятся там как нечто, что новички в вашей кодовой базе видят всякий раз, когда они пытаются изучить их, они находятся в графе зависимостей компилятора и т. д. Программисты должны знать типы и создавать два новых объекта всякий раз, когда они вызывают HasAccess .

А для чего? Чтобы вы случайно не перепутали имя пользователя и идентификатор документа при проверке, имеет ли кто-то право доступа к базе данных. Эта проверка, вероятно, должна быть написана два, может три раза в обычной системе. (Если вы пишете это много, вам, вероятно, не хватит повторного использования кода доступа к базе данных)

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

1 голос
/ 06 октября 2008

Здесь typedef становится полезным в C ++. Вы можете иметь UserID и DocumentID как typedeffed типы и, следовательно, не взаимозаменяемы без приведения, но вам не нужно ничего, кроме краткого примечания для компилятора, говорящего «это должен быть отдельный тип, отличный от других типов, даже если это действительно просто тип X '.

1 голос
/ 06 октября 2008

Интересно, но я подозреваю, что во многих случаях (особенно API-интерфейсы seialization / RPC) это только добавит путаницы / накладных расходов. Также - незначительная деталь реализации, но при таком подходе я бы сделал обертки полностью неизменяемыми, а не просто неизменяемыми «внутреннего набора».

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

string user = "fred";
SomeMethodThatWantsADocument(user);

Это должно скомпилироваться; неявный оператор отменяет всю вашу хорошую работу ...

0 голосов
/ 06 октября 2008

Для меня это похоже на проблему ЯГНИ. Если вы просто делаете это, потому что это МОЖЕТ быть полезным, это обычно не является достаточной причиной для дополнительной сложности. Кроме того, как отмечали другие, именно такие вещи должны ловить юнит-тесты.

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

0 голосов
/ 06 октября 2008

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

0 голосов
/ 06 октября 2008

То, что вы не спрашиваете и не отвечаете, это вопросы, которые лучше всего определяют, важны ли новые типы:

  1. Каков прогнозируемый, реалистичный срок службы этой системы? Если ответ более 2 лет, у вас должен быть хотя бы один уровень абстракции для базы данных и для идентификатора пользователя. Другими словами, ваша база данных должна быть абстрактной, а ваши пользователь и учетные данные должны быть абстрактными. Затем вы реализуете свою базу данных и ИД пользователя в терминах абстрактного определения. Таким образом, если потребности изменятся, ваши изменения будут локальными по отношению к местам, которые больше всего в этом нуждаются.

  2. Каковы выгоды и убытки от использования типа данных userid? На этот вопрос следует ответить с точки зрения удобства использования, выразительности и безопасности типов. Количество созданных классов или дополнительных строк в значительной степени несущественно, если есть очевидные преимущества в удобстве использования и выразительности - ура, вы выиграли. Позвольте мне привести пример явной потери - я работал с иерархией классов, которая содержала абстрактный базовый класс с несколькими конкретными дочерними типами. Вместо того, чтобы предоставлять конструкторы для дочерних классов и соответствующие методы доступа, они создали фабричный метод, который принял строку или поток XML в качестве аргумента и создал соответствующий конкретный класс из этого. Это было настолько потеряно в удобстве использования, что это сделало эту библиотеку болезненной - даже их пример кода пахнул потерей. Хотя я мог построить все, что они предлагали, он казался отвратительным и генерировал время выполнения вместо ошибок времени компиляции для типичных проблем.

0 голосов
/ 06 октября 2008

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

0 голосов
/ 06 октября 2008

Да, иногда это хорошая идея. Но если вы слишком одержимы этим, вы становитесь астронавтом архитектуры.

Что касается аргумента безопасности типов - он повышает безопасность типов, но многие языки прекрасно обходятся без него.

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

Предсказывать будущее слишком сложно, чтобы тратить время на попытки.

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