• Money как Value Object
    0
    Отлично, рад что мы поняли друг друга ;) Просто, глядя на этот конкретный пример, у меня зародились подозрения о слепом использовании каких-то подходов (о чём я писал). Но если это делается с умом, то это конечно другое дело. Будем ждать апдейта и новых статей, и успехов Вам в разработке!

    В общем, как писал SergeyT:

    Помимо пользы, от паттернов проектирования может быть и вред. Бездумное и неразумное использование любого даже самого полезного инструмента или технологии приведет к «абсурду и коррупции». Сколько раз вы сталкивались с проблемой “overengineering-а”, когда для реализации простой концепции использовался десяток паттернов проектирования? В общем, это очередной пример того, что прагматизм и здравый смысл, как всегда является лучшим выбором, и паттерны проектирования – не исключение.

    Да восторжествует здравый смысл, и всем нам будет счастье! :)
  • Money как Value Object
    0
    потому что есть другая система, которая принимает всё в копейках.

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

    Лично я бы преобразовывал суммы сразу при общении с этой другой системой в decimal, только и всего. И удалил бы напрочь «long kopeks» из проекта, как кошмарный сон, чтобы вот вообще никаких следов это безобразия не осталось :)

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

    Эмм… А «концепция»-то в чём заключается? Чем вот по сути эта структура отличается от long? Умножением и делением на 100? Это и есть та «концепция», которую непременно надо куда-то инкапсулировать?

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

    Не объектов, как объектов, которые имеют поведение, а объектов, которые являются структурами данных

    Ну, лично в моём проекте там полно поведения :) Некоторые вещи прекрасно решаются интерфейсами и методами расширения. Я не идеализирую EF, ни в коем случае. Там огромная куча недоработок…

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

    Подчёркиваю, любой паттерн создан для решения каких-то проблем. Если этих проблем нет и не предвидится, любое их использование — это дорога в ад. А если проблема есть, то надо сначала подумать, какими способами можно её решить, собственной головой. Умный не тот, кто прочитал много книг. А то, кто умеет перерабатывать информацию и генерировать новую (если она конечно полезна). Тот, у кого развита интуиция за базе собственного опыта. Какие-либо авторитеты и совершенно ненужные знания обычно не помогают, а вредят интеллектуальному развитию и личному росту. Вот ей Богу, если бы тот разработчик, которого я упоминал, не прочитал ни одной книги, он бы написал намного более качественный и понятный код, а главное — научился бы думать.
  • Money как Value Object
    0
    На самом деле, даже обёртка над decimal может пригодиться, если понадобится унифицировать обработку разных по смыслу данных. Самый простой пример — ToString():
    var items = new object [ ] { someMoney, someOtherValue };
    foreach (var item in items) Show(item.ToString());

    Если бы в Вашем примере был реализован ToString(), в нём был бы хоть какой-то смысл. Хотя, и то этот смысл сомнителен, т.к. необходимость ради одного ToString реализовывать кучу операторов (для превращения Money в полноценный decimal) — это большой вопрос, и зависит опять-таки от контекста, в роли которого выступает проект.
  • Money как Value Object
    0
    Money.FromRubles(10) гораздо лучше чем decimal a = 10m;

    Есть структура, которая делает код более читабельным

    Да Вы, верно, шутите? По-Вашему это более читабельно? Дело не в согласии несогласии, а в фактах. Сколько человек на Земле знают про Вашу структуру Money? И сколько человек на Земле знают про тип decimal? Глядя на запись «Money.FromRubles(10)» у меня сразу же возникает куча вопросов: а как эта структура будет хранить данные — с какой точностью? А не конвертирует ли она, случайно, рубли во что-то ещё в этой ф-ции? А смогу ли я использовать её в мат. выражениях? А насколько она производительна? И т.д. Вы сами посмотрите на этот код через полгодика и будете судорожно пытаться вспомнить, зачем Вы этот Money вообще ввели.

    Представьте, что Вы открыли чужой код. Что для Вас будет более читабельно — вот это: «new DateTime(2015, 4, 5)», или вот это: «5.April(2015)»?

    Ну, так тот разработчик сделал глупости, вероятно

    Самая главная его глупость — это то, что он слепо применял всё подряд о чём прочёл. И здесь я вижу ровно то же самое. Меж тем,, как надо совершенно ясно понимать, для чего создавались те или иные решения, и всегда думать, прежде чем применять что-то, проходя самостоятельно тот путь, который прошли до Вас, но уже гораздо быстрее, т.к. на пути есть указатели. А если Вы сразу переноситесь в конечную точку рассуждений, Вы с очень большой вероятностью оказываетесь не там.

    и расширяема, если потребуется введение валют.

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

    И даже в случае проверки корректности данных (что совершенно не относится к Вашему примеру)… Предположим, что Вам нужно принимать от пользователя, сохранять и отображать Zip-Code, а вы, скажем, работаете с EntityFramework, в котором корректность обеспечивается атрибутами на свойствах модели. Нужна ли в этом случае обёртка над string? Нет. Всё зависит от ситуации, и универсальных на все 100% решений не существует. Никто и никогда не избавит Вас от необходимости думать, когда Вы принимаете решение использовать какой-либо готовый подход.

    Ну, или же, оборачивайте в структуры все подряд: string'и, int'ы, float'ы. Оберните заодно и все объекты Framework'а. А что, вдруг пригодится? ;)
  • Money как Value Object
    0
    Обратите внимание, что конструкторы закрыты

    Без разницы. Money.FromKopecs — ничем не лучше.

    Если вы напишите класс ZipCode, то он станет оболочкой над string и что дальше?

    Я уже цитировал, и процитирую ещё раз:
    Инкапсулируем примитивное значение в объект-значение, который содержит соответствующие защитные выражения и т.д., для того, чтобы гарантировать, что возможно создать только корректные экземпляры.

    Где у Вас проверки на корректность как в случае в ZipCode?

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

    Помнится, я работал с одним молодым разработчиком, который слишком любил читать «умные книжки». И однажды, когда мне пришлось, работать с его кодом, я пришёл в тихий ужас, который у меня не проходил несколько дней. Грубо говоря, у него для деления на 100 использовалась даже не одна структура, а целая иерархия из шаблонных классов. И практически каждый паттерн, который он применил, был применён совершенно не по делу, что сделало его код нечитаемым даже для него самого. И весь код был выброшен в помойку.

    Хорошие решения призваны бороться со сложностью, а не создавать её на пустом месте.
  • Money как Value Object
    0
    Можете начать бросать в меня помидоры, потому что я не осилил это гору комментов, но я скажу вот что…

    В той статье про «одержимость примитивами» сказано:
    «Инкапсулируем примитивное значение в объект-значение, который содержит соответствующие защитные выражения и т.д., для того, чтобы гарантировать, что возможно создать только корректные экземпляры.»

    Уже на этом этапе имела место ошибка в понимании этого текста, т.к. decimal уже гарантирует только корректные экземпляры.

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

    За мой немалый опыт разработчика я усвоил одну истину: самый большой вред — это одержимость не примитивами, а паттернами и «советами умных людей», не понимая их суть. И использование сложных подходов там, где они не нужны по определению.

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

    И поверьте, если у Вас в системе фигурируют суммы в «long kopecs» (что изначально являлось недоработкой проектирования), то запись «decimal money = kopecs/100» будет гораздо очевиднее незнакомому с вашим кодом человеку, чем «var money = new Money(kopecs)», т.к. с Вашим Money он вообще не знаком, и ему придётся заглянуть в него и ужаснуться.

    Ваш класс только тогда начнёт иметь смысл, когда не будет являться оболочкой над decimal (который и так уже всё гарантирует), а будет содержать в себе ещё что-то. Например, код валюты.

    А до тех пор лучше заняться избавлением проекта от «long kopecs», а не потворствовать этому. А эта абстракция, раз уж она уже есть, пусть существует в виде оболочки над decimal, может, когда-нибудь и пригодится…
  • DDR4 выйдет в продажу уже в следующем месяце
    0
    Да по сути, в любом случае на общую производительность, судя по тестам, 4-канальность не влияет… Поэтому и в DDR4 я особого смысла не вижу, если latency будет той же…
  • DDR4 выйдет в продажу уже в следующем месяце
    0
    Сейчас пошукал в инете и выяснил, что всё-таки не будет он на Z87 работать… Получается, что выбора только два: покупать сейчас LGA2011 или ждать год. Иначе не будет никакой возможности для роста.
  • DDR4 выйдет в продажу уже в следующем месяце
    0
    В том-то и дело, что к сожалению, Z97 выйдет только во второй половине 2014-го, насколько я читал… Надеюсь, Broadwell хотя бы не откажется работать на Z87…
  • DDR4 выйдет в продажу уже в следующем месяце
  • DDR4 выйдет в продажу уже в следующем месяце
    0
    Да просто я выбираю сейчас между LGA1150 и LGA2011… И задумался: а стоит ли ждать DDR4, если я сейчас возьму LGA2011…
  • DDR4 выйдет в продажу уже в следующем месяце
    0
    Ну вообще, я другие результаты видел… В синтетике пропускная способность выше почти в 2 раза, как и должно быть…
  • DDR4 выйдет в продажу уже в следующем месяце
    0
    А мне вот интересно, что будет лучше 4-канальная DDR3 на LGA2011 или 2-канальная DDR4? Мне сейчас как раз апгрейд светит, и получается, что если это будет равносильно, то можно преспокойно продолжать сидеть на DDR3 ещё ооочень долго…
  • Как я завалил собеседование в Twitter
    0
    Вот когда мне с такими задачами приходится сталкиваться, я их решаю на ура. И эту решил, без спойлера и без бумажки. Но ни на одном собеседовании я подобное решить не способен. Не учитываются психологические факторы, различные у разных людей. К тому же, на практике всё, что Вы перечислили, встречается с вероятностью 0,0000001%. А паттерны, кстати, лично я не очень уважаю. А уважаю умение думать головой и знание тонкостей языка. Только вот заставлять думать головой на собеседовании — это извращение, потому что у человека даже в течение дня IQ может прилично скакать. Смотреть надо по опыту и по делам, а не делать выводы из решений подобных задачек.
  • Как жить без const?
    0
    Спасибо, не знал про него, поправил.
  • Как жить без const?
    +1
    Воспользовался Вашими пожеланиями и убрал из текста статьи всё касаемо вариативности, чтобы не запутывать лишний раз (только в коде оставил). Но кое-что дописал (добавил ещё один подход). Теперь вроде всё в полном объёме и всё по делу :)
  • Как жить без const?
    0
    Дописал статью, добавив туда «Адаптер». Только убрал из вашей реализации наследование Vector и IVector от IReadOnlyVector, т.к. в этом случае становится возможным беспрепятственно передавать Vector и IVector в методы, которые принимают IReadOnlyVector, без вызова AsReadOnly(). А такое я уже описал в пункте 2. В общем, спасибо в любом случае, приятно было подискутировать.
  • Как жить без const?
    0
    Необязательно. Вы декорировали ваш читающий класс методами записи.

    Определения декоратора без динамики я не встречал. А вообще, как хотите называйте, хоть страусом :) Я привык смотреть на код и думать своей головой, а не навешивать на всё подряд ярлыки, пытаясь определить их названия, или применять на практике эти подходы, не понимая их сути и не подвергая их критике и осмыслению, чем страдают многие разработчики. Универсальных шаблонов не бывает, универсальным может быть только мышление разработчика, способного мыслить за рамками шаблонов.

    MethodImplOptions.AggressiveInlining, если хотите дать совет JIT.

    Замечательно, спасибо, буду знать :) Но это только подтверждает преимущество моего подхода. Можете записать его в коллекцию паттернов :)
  • Как жить без const?
    0
    И могу с увереностью сказать, что вы реализовали Декоратор

    Декоратор служит для динамического подключения поведения, у меня ничего динамического тут нет.

    ваша read-only версия читает данные быстрее, т.к. нет дополнительного вызова, в то время как моя full версия читает быстрее по тем же причинам.

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

    На самом деле, я бы не рекомендовал пользователям моего подхода делать то, что я сейчас сделал. Т.к. это вносит путаницу. Лучше просто привыкнуть к свойству Reader.

    Я рассматривал данный подход в контексте аналогии с cast из C++. А cast не предусматривает никаких дополнительных вызовов, это проверка в compile-time и максимум производительности. Поэтому я и предложил такой подход, чтобы не было ощутимых потерь. Кроме того, этот подход можно использовать и в самом C++, когда нужен более надёжный код, но каждая миллисекунда на счету. Кстати, там для получения reader'а вообще можно поставить __inline.
  • Как жить без const?
    0
    Я опять поторопился… Вот так:

    struct Vector<T> : IVector<T>
    {
        ...
        public T this[int nIndex]
        {
            get { return _reader[nIndex]; }
            set { _vector[nIndex] = value; }
        }
        ...
    }
    

    Т.е. без наследования от IVectorConst (это помешает передаче быстрого reader'а в методы).
  • Как жить без const?
    0
  • Как жить без const?
    0
    Извиняюсь, я что-то не то написал… «Запихать VectorConst в Vector» — это чтобы заставить разработчика использовать исключительно интерфейсы (об этом я писал в статье).

    А чтобы убрать необходимость в вызовах Reader достаточно вот этого:
    struct Vector<T> : IVector<T>, IVectorConst<T>
    {
        ...
        public T this[int nIndex]
        {
            get { return _reader[nIndex]; }
            set { _vector[nIndex] = value; }
        }
        ...
    }
    


    То же самое, что у Вас, только отсутствие вызовов через интерфейс, быстрый readonly-объект, контравариантность IVector и разделение ответственности.
  • Как жить без const?
    0
    можно запихать его в Vector.


    Хотел сказать: «можно запихать VectorConst в Vector»
  • Как жить без const?
    0
    Да, это так. Сознательно. Если вызывать свойство Reader каждый раз при любой операции получится то же, что и у Вас. А при передаче VectorConst в другой метод, Reader или оператор преобразования вызовется только один раз. Получается, что, как ни крути, это выгоднее «Адаптера».

    Если свойство Reader настолько уж раздражает, можно запихать его в Vector. пометив как private, и перенаправлять к нему реализацию IVectorConst (или Вашего IReadOnlyVector). И даже это всё равно будет лучше «Адаптера», т.к. вызовы будут происходить не через интерфейс и останется разделение ответственности.
  • Как жить без const?
    0
    Я не хочу холивара, но вынужден немного не согласиться…

    Я предлагаю избавиться от дублирования кода реализации

    В данном случае, дублирования нет.

    и добавляет сложности в поддерживание кода, т.к. нужно синхронно вносить изменения во все реализации

    Чтение и запись — разные вещи и внешние методы не повторяются. А если будут появляться скрытые (именно скрытые) общие методы, то опять-таки «Адаптер» — не единственный выход. Можно сделать оболочку не над Vector'ом, а над данными. Но и этого в 99 случаев из 100 делать не придётся, т.к. эти потенциальные методы будут readonly и скрывать их, скорее всего, не придётся.

    А в поддержке этот подход, боюсь, будет даже проще благодаря дополнительной декомпозиции.

    Паттерны GoF — не есть конечная истина и польза, скорее наоборот :)
  • Как жить без const?
    0
    Дублирования данных не происходит, дублируется только ссылка на данные, что для качественно написанных объектов ни к чему плохому не приводит. И даже наоборот: когда Vector уже не нужен, он спокойно может быть уничтожен, в то время как VectorConst может существовать дальше. У Вас он в такой ситуации не уничтожится, т.к. на него есть ссылка в ReadOnlyVector.

    Но они не настолько снижают производительность...

    К сожалению, в ряде случаев настолько. И Вы мало того, что дублируете вызовы, Вы эти вызовы осуществляете через интерфейс, что ещё в разы(!) делает их медленнее.

    Для задач, совершенно не требовательных к производительности, это приемлемый вариант, не спорю. Однако я всё равно не вижу причин его использовать. Этот вариант может быть вынужденной мерой, когда объекты уже написаны и Вы не имеете доступа к исходникам. Или же просто нецелесообразно тратить время на переписывание объекта. Адаптер — это костыль.
  • Как жить без const?
    0
    Здорово Вы всё расписали! Да, статья для Миши конечно… Впрочем, если все три лица находятся в одном, то это всё тоже имеет смысл. Мы и сами довольно часто отстреливаем себе ногу :) А по разным сборкам — наверное действительно уже слишком. Хотя, наверняка и такое практикуется… Сделали бы const уже, сколько проблем бы это устранило…
  • Как жить без const?
    0
    Почему?

    А лишние вызовы по-Вашему бесплатны? Я отказался от скрытия readonly-структуры, чтобы можно было к ней напрямую обращаться, а тут такое…
  • Как жить без const?
    0
  • Как жить без const?
    0
    Насчёт коллекций извиняюсь, мне померещился IReadOnlyCollection в коде :)

    ReadOnlyVector у Вас является оболочкой над Vector, но методы-то в нём Вам всё равно реализовывать. Это и есть то дублирование, которого у меня как раз-таки и нет. Но если так трудно вызывать Reader, можете мысленно и в мой код добавить такое наследование, сути это не меняет.

    Кстати говоря, я в статье постоянно упоминал про производительность, а Ваш wrapper — это мягко говоря, не очень эффективное решение.
  • Как жить без const?
    0
    const, применительно к переменным внутри метода — это совершенно другая опера. А DateTime компилятор не даёт делать const потому, что что он не является «compile-time constant», для его инициализации нужно вызвать конструктор, а сделать это можно только в runtime.
  • Как жить без const?
    0
    1. Если нужно в строке просто заменить один символ, то StringBuilder не спасёт Вас от копирования всей строки. А насчёт char[]… Для этого как раз мой подход и может пригодиться.

    2,3 В статье везде говорится о константности значений. Да, Vector изменяем. Но не IVectorConst. И если метод принимает именно IVectorConst, то без даункаста он не сможет изменить данные.

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

    5. Тут речь о том, чтобы иметь возможность и изменять состояние объекта, и когда надо запрещать изменения чужим методам. Объект, который Ва описали — это неизменяемый объект, наподобие string. Это хорошая практика, но у неё есть и минусы, о которых я упомянул в первом разделе. А делать можно любым из трёх способов, всё зависит от того, насколько производительный и насколько строгий код Вы хотите иметь… 3-ий вариант — это и то, и другое. Но слишком усложнять простой код тоже не стоит, я думаю.
  • Как жить без const?
    0
    Это ровно то же самое, что и у меня. Только у меня в ReadOnly сохраняется ссылка на сами данные класса (T[]), а не на сам класс. И вместо моего свойства Reader у Вас AsReadOnly(). И ещё у Вас Vector наследует от IReadOnlyVector, что заставит Вас реализовывать этот интерфейс дважды. И повторюсь, не всё является обычными коллекциями. А гарантировать на 100% из-за Reflection'а никто и не может, просто так надёжнее, потому что обойти это сложнее. Да и код разделён на два класса, что делает его читабельнее.
  • Как жить без const?
    0
    Да, согласен. Но я здесь рассматриваю более общий случай. То, что в примерах вектор, — это для простоты. Это могла бы быть и матрица, к примеру, для которой придётся создать свой readonly-интерфейс. К тому же, вышеозначенные интерфейсы так же не спасут от даункаста, если класс находится в той же сборке или видим извне.
  • Замыкания на переменных цикла в C# 5
    0
    Отличная статья, спасибо! Жаль, что разработчики C# идут на поводу горе-программистов… Для меня лично раньше всё было очевидно, а сейчас уже нет. Тем более, что for остался прежним. Такой подход идёт вразрез с логикой…
  • Композиция vs наследование
    0
    Я написал выше (в ответе Dair_Targ'у) пример ситуации. К какой архитектуре можно прибегнуть в данном случае? Хотя, если Вы говорите о C# или Java, то там композиция конечно намного эффективнее. Но, имхо, в ряде случае, она там — не пример удачной архитектуры, а «костыль» в отсутствие множественного наследования. Но даже в этих языках, если ни один их обозначенных плюсов не используется, то (опять же, имхо) наследование предпочтительнее. Оно только упросит код.
  • Композиция vs наследование
    0
    Возможно спорный, да. Но с другой стороны, почему не может быть ф-ции, которая выполняет какую-либо сложную операцию, основываясь на нескольких поведениях объекта, и при этом, какое-то одно из этих поведений является единственно необходимым, а другие являются вспомогательными и служат для какой-то коррекции результата. При наследовании мы можем «отфильтровать» неподходящие объекты на этапе компиляции. Более того, мы можем создать некий условный пустой класс, наследующий сразу от нескольких поведений и «фильтровать» таким образом сразу по нескольким поведениям, обеспечив гарантию того, что в ф-цию будут приходить объекты, обладающие всеми необходимыми свойствами. При композиции для этого всё равно придётся прибегать к наследованию от дополнительных интерфейсов…
  • Композиция vs наследование
    0
    Если подобъект мал, то его имеет смысл хранить не через указатель, а напрямую.


    В этом случае возникает проблема, если нужно в производных классах (от класса-хозяина) подменять поведение. Ну, вернее, подменить-то его можно (если есть указатель), но включённый напрямую в базовый класс подобъект просто останется висеть мёртвым грузом…
  • Композиция vs наследование
    0
    К сожалению, эту истину знают не все и часто слепо следуют каким-то правилам и паттернам. Я просто постарался показать, что религия в разработке — не есть хорошо, и сформулировать плюсы и минусы.