Как стать автором
Обновить

Комментарии 115

ахахах ржунимагу, соседей позвали сидим над заголовком угораем, участковый зашёл от смеха табельным мне мезицен на ноге прострелил, сижу в травмпункте пишу с утки
С шутками тут все хорошо, а за глупости — наказывают.
А хотел всего-лишь — поднять настроение.
Что-то пошло не так.
Со стилистикой слегка промахнулся.
Тут давно всё плохо.
утка-то здесь причем?
Он с нее пишет
Бедное животное.
Функция getCustEmail была унаследована от функции getCustName, когда в приложении появилась поддержка email-адресов. Наследование кода подобным образом позволяет избежать случайного введения в программу новых багов.


Вы обзываете наследованием простое копирование кода с внесением правок, чем извращаете саму эту идею. Ваш пример — максимум рефакторинг, и то какой-то бессмысленный и беспощадный.
Наследование же как раз наоборот позволяет избавиться от дублирования кода и избежать потенциальных ошибок.
Если бы вам предоставили, скажем, готовый класс CustInfo с полем custRec и методом getCustName(custRec), а вы унаследовали от него свой класс MyCustInfo и расширили его методом getCustEmail(custRec) — вот это примерно то, что обычно подразумевается под наследованием.
Очень уж тонкая грань между юмором и ересью получилась.
Невероятно тонкая, почти прозрачная, не разглядишь)) <да да сарказм)>
Серьезно? А это единственное, что вас смутило :)?
Нет, дальше я уже всерьез не воспринимал :)
Дальше? «Ущербно-ориентированное программирование» вас на мысль не навело?
НЛО прилетело и опубликовало эту надпись здесь
Боюсь если покажу эту статью своим менеджерам они не поймут юмора и чего доброго потребуют соблюдения вышеописанных принципов. Жисть не легка однако порой…
Очень хорошая сатира получилась. Про написание документации за две недели до увольнения это просто шик.
«УОП» имеет еще одно толкование — Убейтесь ОПчтонибудь
заминусовали адепты УОП? :)
Ага! Знаем, используем, продвигаем! :) Спасибо за настроение!
Надо будет на досуге пародию с уклоном в системное программирование написать
Ваша статья напомнила мне вот этот classic WTF:

enum BOOL { FALSE, TRUE, FILENOTFOUND }

Спасибо за поднятое с утра настроение!
Шикарно!
Полный код (Visual Studio 2015).
image
Нормальная такая нечёткая логика. ;-)
Относительно перегрузки. Иногда правильнее будет сделать функцию с одним запросом в БД, которая вернет все поля, чем писать три (или более функции) в каждой из которых будет по запросу. Естественно, если требуется получить более чем одно поле.
Иногда правильнее работать с данными как с объектом, а объект — шарить. Иногда правильнее два раза в базу сходить. Иногда ещё совсем неправильно возвращать все поля из базы, потому что там бинарник на 100 килобайт или вообще, зачем тебе информация, которая тебе никогда не будет нужна. А иногда ещё…
Иногда правильнее не отвечать на глупые комментарии
И придерживаться своих же советов.
lazy load же
Мне кажется важную практику упустили
Continuous deploy
Это выкладка кода на боевой сервер желательно копированием по ftp, если application серверов больше одного то для синхронизации можно делать копирование силами нескольких участников на счет три. Последующие исключения исправляются по мере их немедленного появления или в понедельник утром, так как традиционно процедура деплоя назначается на конец рабочего дня пятницы, поскольку производительность разработчиков в это время снижается из-за усталости и кодить они толком не могут.
Уместнее здесь было бы про затянутое внедрение написать, наверное :)
О чем вы говорите, о каких исключениях?

try
{
   ....
}
catch 
{ }
В CI есть такая практика как Extremal Continnuous Deployment, когда каждый прошедший билд тут же идет на боевой сервер. Но естественно, следует понимать чем это может быть чревато. Кому интересно, могут почитать блог человека практикующего такой подход.
Ну так да, автотесты же бесполезны, а тестировщики всё равно что-нибудь да пропустят, какая разница.

А вообще — подход хороший, когда есть master/devel ветки продукта, и клиенты, осознающие ради чего они пользуются devel-версиями и включают автоматическое обновление.
Кстати про тестирование тоже не слова:
Тестирование
Проверка работоспособности приложения заказчиком после релиза. Традиционная методика в данном случае отправка разработчикам приложения сообщения «Ничего не работает» или аналогичный звонок. Хорошим тоном является отправка ответного сообщения «На моей машине все работает» после чего уже может начатся конструктивная фаза.
Тогда ещё добавим пару слов об отладке :)

Отладка
Баг записывается в jira и задача становится отложенной.
Какая jira, о чем вы? У нас до сих пор потерялся листочек с 8ю некритичными багами с неиспользуемого модуля одного старого проекта…
Вы ещё забыли пункт про скрипт (автоматизация же), который конфиги копирует во временную папку, потому что новый код их затерёт, а потом обратно копирует поверх нового кода.
Вы напомнили одно чудище, существующее в одном моём проекте, которое хитро делает merge конфига из нового RPM-пакета с тем, что ввёл пользователь. Жаль альтернативы более адекватной не могу найти, может посоветуете чего?
Хранить настройки в Mercurial, из RPM — помещать в одну ветку, свои изменения — все в другую, после обновления делать слияние?

В крайнем случае дописать к этому свой mergetool: при наличии общего предка слияние не должно быть слишком хитрым.
Слияния делать нежелательно, т.к. в конечном счёте это приводит к полной или частичной потере существующего конфига или в недоконфигурировании вновь созданных параметров.
Потеря с VCS?! Кроме того, weirded спрашивал, есть ли что‐то для слияние настроек, подобное тому, что он видел. Я указал альтернативу, которой сам пользуюсь. Очень частые изменения настроек: исправление ошибки в комментариях, единственный (он же по‐умолчанию) стал не единственным и был вынесен в настройку: с этим VCS отлично справляется. С бо́льшими изменениями есть больши́е шансы нарваться на конфликт при слиянии, но в этом случае вы будете видеть, какая часть файла была написана человеком, а какая — пришла из RPM.

Кроме того, если настраиваемый компонент важен, то вы должны проводить тестирование. Возможность review после слияния тоже никто не отменял. Да и если вам не хочется полагаться на VCS, вы можете заставить VCS всегда сливать с помощью vimdiff. При этом вы опять же будете видеть, какие настройки пришли откуда.
Если бы видел, я этот ужас писал.

Конфиг в виде ассоциативного массива на bash, сразу же с описанием виджетов для отображения в консольной менюшке на dialog. Пример: github.com/strizhechenko/bash-config/blob/master/config

А мерджит его вот такое чудище: github.com/strizhechenko/bash-config/blob/master/merge_config.sh

Вы предлагаете пользователю делать vimdiff после слияния?:)

Если пользователь взял и поменял конфиг в /etc, то он не только пользователь, но и администратор. Я не вижу ничего плохого в том, чтобы администраторы использовали vimdiff для проверки корректности слияния или для самого слияния.

Кроме того, если вы не хотите писать слияние по сложным правилам, специфичным для каждого пакета (и при этом справляться с проблемами вроде «что делать, если пользователь пропустил пару‐тройку [десятков] релизов?»), то единственная вещь с алгоритмом, работающим для всех текстовых файлов, которая мне приходит в голову — это VCS.
Её же можно заставить использовать для слияния конфигов ваш скрипт, если нужно, при этом не рискуя испортить файл настроек навсегда (что возможно, если не делалось резервное копирования, а в вашем алгоритме слияния ошибка).
В принципе, вы описываете некую реализацию миграций. Вообще, это именно через миграции и делается — если есть дефолты, добавляем в рабочий конфиг дефолты, если есть настройки, для которых обязательно нужно выставить значения — у пользователя запрашиваются значения этих настроек. Т.е. по сути, то же самое, что при установке.

Но это хорошая практика. В отличие от попыток «защитить» конфиг, вместо того, чтобы использовать какой-нибудь версионируемый config.default и игнорируемый config.
Зачем временная папка? Достаточно переименовать файлы, а потом обратно.
Нет, это уже слишком хорошая практика. Как известно, лучшее — враг хорошему.
В раздел «Документация» стоило бы добавить пару рекомендаций по написанию этой самой документации.

Как понятно из названия, документация должна документировать код. При этом нужно избегать следующих малораспространённых (к сожалению) ошибок:
1) Не нужно писать, что по задумке автора должен делать этот код. Вдруг задумка или требования поменяются.
2) Не нужно описывать, что делает код в целом. Любому программисту это должно быть понятно из кода. Лучше сосредоточиться на частностях и деталях, например:
for (int i = 0; i < sz; ++i) { // цикл от 0 до sz
4) Никогда не нужно писать в комментариях, где и как используется данная функция или переменная. Это всегда можно выяснить поиском по проекту.
5) Не следует описывать параметры функции и возвращаемый результат. Это и так интуитивно понятно любому, кто использует данную функцию. В любом случае, достаточно того, что это понятно автору кода.
И если вы вдруг пишете систему документирования, а она в ранней альфе и всё валится, то нужно документировать именно в ней, чтобы программист мог отвлечься от монотонного кодинга на светлые мысли о суициде. Потому что ведь двух зайцев сразу убить — и документацию написать, и систему потестировать.
Псевдокод можно было не писать. У каждого свои жуткие ассоциации с данными примерами :) Особенно смешно вышло с наследованием, а дальше уже слегка мутно
Главное, чтобы код был интуитивно понятен.
var name1 = name + "1";
var name2 = name + "2";
var name3 = name + "3";

if ( doc.Bookmarks.Exists( name1 ) && 
     doc.Bookmarks.Exists( name2 ) && 
     doc.Bookmarks.Exists( name3 ) ) {
         // ...
}
«4/15/96 git» — так с 96 года, получается, такой код живет!
И впрямь долгосрочное использование.
Про недостижимый код бы еще

function SomeFunc(...){
    ....
     return $a ? true : false;

     throw new Exception('Пыщь пыщь алярм!');
     return false; 
}


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

If(false) {

... 9000 строк кода ...

}
Подход if(false) { ... } иногда может быть полезен, поскольку код в нем все равно проверяется на синтаксическую и семантическую корректность. В результате исключается ситуация, когда код нужно срочно раскомментировать, а он не компилируется, потому что кто-то отрефакторил использованные в нем классы \ методы.
Да, в статье сильно не хватает пункта про комментирование ненужного кода, т.к. вдруг он снова понадобится, а искать в git'е долго и неудобно.

Ну и никаких тестов и деплоев, конечно. Только хотфикс, только на продакшне, только под рутом.
через vim, а особо срочные задачи — через echo >>
Ну и, как уже говорили про деплой, это тоже только в пятницу вечером или в субботу ночью, с мобильника в клубе.
Иногда и через echo >, а потом случается «ой!».
Я бы еще вспомнил про «суперкласс». Суперкласс — это самый важный класс в системе. Без окружения он не имеет смысла. Он знает всё. Он самый большой. Его чаще всего меняют. Он имеет непредсказуемое название (например Binder или ShoppingCart). В момент его рождения никто не мог предсказать то, что это будет ключевой компонент системы.
Ну зачем придумывать название. Это же классический User.
it depends
it all depends… on User.
Ха! Я как-то в нашем проекте такой суперкласс вводил — с целью упрощения дальнейших изменений. До моего рефакторинга тем же самым занималось около 30 классов, подписанных на события друг друга, причем подписывать их надо было в строго определенном порядке. Диаграмма последовательности с трудом уместилась 10м шрифтом на лист A4.

Как называется такой паттерн? Я бы предложил черный ящик — потому что ничего не понятно, и внутри что-то тикает, но это название в статье уже занято.
GodClass?
Нет, я про ситуацию с 30 классами, которые можно менять только совместно.
Объектно-событийная модель? Объекты сами должны знать, каким объектам они посылают события, от каких объектов они событий ждут, в каком порядке это должно происходить и зачем. Для повышения эффективности семантика очередного события может меняться в зависимости от предыдущих. Предсказать, как будет происходить процесс, программист не может, но это и не нужно: ведь всё работает правильно.
Сильная связанность.
Минус: сильная связанность. Но по сравнению у god-entity абсолютно такой же минус.
Плюс: проще дебажить (имхо) из-за того, что сразу понятно, в какой функциональности сбой.

Вообще, тут есть вопросы. Посмотреть бы на эти классы.

Но то, что сделали вы (функциональность 30 классов в одном) — это уже даже слишком плохо, чтобы обсуждать.

Взять какую-нибудь Symfony2 Standard Edition и диаграмму классов нарисовать, а потом ещё классы посчитать. Будет сильно страшнее, чем вы написали.
Но то, что сделали вы (функциональность 30 классов в одном) — это уже даже слишком плохо, чтобы обсуждать.
Откуда вы там функциональность 30ти классов-то взяли и почему нельзя обсуждать?
Во-первых, функциональность в классах не измеряется — они у всех разные.
Во-вторых, там этой функциональности всего на 6 классов набежит, не больше.
В третьих, дальше планировалось получившийся код разбить по-новой, но, так как он вдруг стал правильно работать, я решил его дальше не исправлять…
До моего рефакторинга тем же самым занималось около 30 классов
Но кто сказал, что эти 30 классов были нужны?
Вы. Вы же не сказали, что этим занималось, скажем, 10 классов.

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

Ну и, как говорится, «happy debugging, guys».
Самое страшное — когда этот класс имеет название Form1
Не правда, самое страшное — когда этот класс имеет имя Utils.
Самое страшное

textBox1
textBox2
textBox3
textBox4

textBox46

и ручная выгрузка их значений в модель данных.

В одном проекте, который я унаследовал при поступлении на работу была глобальная переменная isUtilCall. На мой вопрос: "зачем она нужна?" — получил ответ, что я пойму позже. В последующий год я всё больше ценил то, что предыдущий коллега не поддался искушению дать мне часть правды, а подготовил к практической непознаваемости всей семантики этого механизма.

Приветствуются комментарии (обязательно на русском) в стиле:
// пробуем авторизоваться из запроса
// не получилось, бывает
// тогда попробуем из кук
// та же беда
// используй силу, Люк! перенаправляем на главную
// эта строчка очень нужна, поверь мне, друг
// на поиск этого решения я потратил 6 дней, СДОХНИ, БАГ!


Ну и обязательно (для удобства коллег и поиска по коду) нужно префиксировать всё:
fnFoo — метод и функция,
aRequest — массив
oUser — объект
sName — строка


И называть надо всё как можно понятнее, т.к. не все по-английски говорят:
shoppingCart
korzina
Мне «нравится» вот такой стиль именования

public void ProcessCustomerEntity(CustomerEntity customerEntity, UserEntity userEntity1, UserEntity entity2) 
{
   ...
} 


Всё ведь понятно, правда? =)
А что именно здесь непонятно? Тут сами параметры метода выглядят странными. Есть подозрение, что в комментарии просто постараются оправдать это.
Ни одно название не передает никакого смысла в контексте бизнес-логики.
function createInvoice(Account $account, $amount)
{
   ...
}


Или я вас неправильно понял?

P.S. У каждого уровня приложения своя бизнес-логика. Одну диктует менеджер проекта, другую — техдир, третью — сам программист.
то что, не понятно что это за аккаунт.
createInvoice(Account $customer, Money $amount)
{
    ...
}
Почему это непонятно? Для сферического метода в вакууме — да. Но кроме этого у вас ещё есть класс, пространство имён и модуль. Мне обычно этого достаточно, чтобы не комментировать метод банальным «Создаёт инвойс для аккаунта плательщика на заданную сумму».

Аккаунт не может быть клиентом. Деньги не могут быть количеством. Плюс, не факт, что мы работаем именно с аккаунтом клиента, а не с любым аккаунтом. Если хочется работать с аккаунтам клиента — используйте специфический класс, например CustomerAccount.

То, что написано в вашем примере это:
а) Двусмысленность — класс параметра говорит, что это аккаунт, а имя параметра говорит, что это клиент (например, пользователь или какая-то другая сущность).
б) Конкретизация, требующая в таком случае дополнительной конкретизации (имя метода перестаёт соответствовать принимаемым параметрам). В моём примере предполагалось, что эта неопределённость разруливается именем класса, в котором реализован метод и именем неймспейса, в котором реализован класс.
Аккаунт не может быть клиентом.

Не факт, зависит от потребностей домена.
Плюс, не факт, что мы работаем именно с аккаунтом клиента, а не с любым аккаунтом.

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

martinfowler.com/eaaCatalog/money.html.
Не факт, зависит от потребностей домена.

В таком случае, странно при наличии договорённости, что клиент — это счёт, относить его к классу Account.

Либо Customer $customer (в нашей системе любой счёт — клиент), либо Customer extends Account (в нашей системе любой клиент — счёт, но не любой счёт — клиент), но в итоге всё равно Customer $customer.

Хотя лично я против использования таких договорённостей на уровне домена. Это сильно ограничивает возможное расширение в пределах того же домена. Например, захочется ввести несколько счетов для одного клиента (что достаточно обычно — бонусный счёт, кредитный счёт, дебетовый счёт) и получим гору проблем.

Потому и принимаем по интерфейсу Account, но в контексте данного метода это не абстрактный аккаунт, а именно клиент, т.к. мы создаем какаю-то счет-фактуру.

Счёт фактуру может создавать кто угодно кому угодно. Это зависит от специфики сайта. Зачем конкретизировать то, что делает итак вполне конкретную вещь — выставляет инвойс на нужную сумму нужному аккаунту. Я не говорил, что это выставление счёта клиенту.

Т.е. ваш пример — просто конкретизация на основе каких-то ваших предположений. Я эти предположения не делал.

martinfowler.com/eaaCatalog/money.html.

Об этом и говорю, деньги — это не количество, а количество в определённой валюте. Разная смысловая нагрузка. Количество в определённой валюте — это Money $money. В данном случае метод может изначально принимать сумму, изначально сконвертированную в валюту счёта. Т.е. это тоже некие предположения, о которых речи не шло.

Что получаем в итоге — вы в конкретном методе, выставляющем любому счёту инвойс на конкретную сумму (валют в системе нет), прочитали метод, выставляющий инвойс клиенту на конкретную сумму в произвольной валюте.

Разве это беда параметров метода? Это беда разгулявшейся фантазии.
в нашей системе любой счёт — клиент.
в нашей системе любой клиент — счёт, но не любой счёт — клиент

Про это и речь. Все зависит от потребностей домена.
Счёт фактуру может создавать кто угодно кому угодно.

Счёт-фактура выставляется продавцом покупателю. Wiki.
конкретную сумму (валют в системе нет)

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

Простите если жестко, но только вашей. Я привел абстрактный пример. Вы сделали из него выводы относительно вашей системы.
А вообще спорим мы не о том. Я в примере показал только то что именовать аргументы в соответствии с именами их классов, имхо, масло-масленное и дурной тон.
Счёт-фактура выставляется продавцом покупателю. Wiki.

А вы говорите, что счёт выставляется клиенту.

Если счёт всегда выставляется покупателю, то зачем конкретизировать, что счёт покупателя? Или всё-таки продавца? Или всё-таки я говорил о системе, когда клиенту моего сервиса выставляется счёт и в этом случае уточнение, что счёт клиента не имеет смысла вдвойне? В этом конкретном методе я этого не уточнял. Возможно, уточнение есть в имени класса или неймспейсе.

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

Т.е. у нас есть виртуальная валюта, не пополняемая извне, но мы всё равно таскаем везде валюту? Бессмысленно.

Я привел абстрактный пример. Вы сделали из него выводы относительно вашей системы.

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

Я в примере показал только то что именовать аргументы в соответствии с именами их классов, имхо, масло-масленное и дурной тон.


Именовать аргументы классом — это хорошая практика:

\Dbal\Driver\Configuration $db — плохо
\Dbal\Driver\Configuration $dbConfiguration — хорошо

string $country — плохо
string $countryName — хорошо (уточняем класс строки — имя, хоть он и не является отдельным классом в коде)
при этом $countryString — плохо (но лучше, чем $country), т.к. всё ещё не даёт понимания, что это за строка.

Account $customer — плохо,
Account $customerAccount- хорошо.

UserGroup $owner — плохо,
UserGroup $ownerGroup — хорошо.

addUser(User $user) — хорошо,
setAccount(Account $account) — хорошо,
setCustomerAccount(Account $account) — хорошо,
setCustomerAccount(Account $customerAccount) — хорошо.

Часто оказывается, что и
User $owner — плохо
User $friend — плохо
если в системе присутствуют классы, вводящие двусмысленность (UserGroup, UserFriend, и т.п.)

В итоге именование с постфиксированием классом объектов всегда является более выгодным решением, т.к. в случае коллизии вам либо всё равно придётся играть с уточнениями постфиксированием, но будет каша, либо в итоге всё вообще встанет с ног на голову:
function createInvoice(Account $customer, $amount)
{
   $user = $customer->getUser();
   Mailer::create()->send($customerUser->getEmail(), 'You have new incoming invoice.');
}


В моём варианте это будет выглядеть так:
function createInvoice(Account $account, $amount)
{
   $user = $account->getUser();
   Mailer::create()->send($user->getEmail(), 'You have new incoming invoice.');
}


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

$user
$userAccount
$userGroup
или
$account
Money $accountBalance (интуитивно понятно)
Currency $accountCurrency
Contacts $accountContacts
А вы говорите, что счёт выставляется клиенту.

Прочитать по ссылке вы не удосужились видимо:
Счёт-фактура выставляется (направляется) продавцом (подрядчиком, исполнителем) покупателю (заказчику) после окончательного приема покупателем (заказчиком) товара или услуг. © Wikipedia.
А customer это как подсказывает Google Translate клиент, заказчик, покупатель.
Вы в ответ на мой пример привели свой, уточнив, что в моём примере непонятно, что это за аккаунт.

Не понятно аккаунт от которого выставляется счет фактура, аккаунт которому, или может это вообще корреспондент через которого?
Именовать аргументы классом — это хорошая практика

Это только ваше имхо и не более. Специально открыл посмотрел Э. Эванса по этому поводу и что то не увидел такой практики при построении домена. На уровне приложения да, есть такое, но не в домене.
Прочитать по ссылке вы не удосужились видимо:
Счёт-фактура выставляется (направляется) продавцом (подрядчиком, исполнителем) покупателю (заказчику) после окончательного приема покупателем (заказчиком) товара или услуг. © Wikipedia.
А customer это как подсказывает Google Translate клиент, заказчик, покупатель.

Ну и какой смысл в этих лекических играх в коде? Если клиент=покупатель, а инвойс всегда выставлется клиенту=покупателю, то почему вам непонятно, на чей аккаунт выставлен инвойс, и зачем уточнять, что на аккаунт клиента? При этом это в абстракции счёт выставлется клиенту. А в ситуации конкретного продавца счёт может выставляться его клиенту или ему самому от поставщиков и т.п.

Я как раз считаю, что для моего конкретного сервиса у счёта-фактуры могут быть разные получатели — мои пользователи, я сам, некие лица, которые не являются частью взаимодействия я <--> пользователь и т.п.

Т.е. в итоге вы зачем-то сначала уточнили, чей счёт (вам это было почему-то непонятно), потом сказали, что уточнения излишни. Странно.

Не понятно аккаунт от которого выставляется счет фактура, аккаунт которому, или может это вообще корреспондент через которого?

С абстракиями вы знакомы? С интерфейсами?

interface InvoiceInterface
{
    public function createInvoice(Account $account, $amount);
}

class InvoiceToUser implements InvoiceInterface
{
    public function createInvoice(Account $account, $amount)
    {
        ...
    }
}

class InvoiceFromVendor implements InvoiceInterface
{
    public function createInvoice(Account $account, $amount)
    {
        ...
    }
}


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

Единственное, чего я не могу понять, почему в вашем примере нет кода услуги. Ведь от этого налоги зависят.
Сначала поймите разницу между доменом и его реализацией. Потом перечитайте Эванса и подумайте, всегда ли надо уточнять, с каким счётом вы работаете и не следует ли это автоматичеки из домена или модуля, в котором идёт реализация этой функциональности. Т.к. как раз Эванс говорит о том, что всё лишнее надо выбрасывать.

Понимать, что $customer — это не клиент, а его счёт — лично для меня, лишнее.

И не путайте тайп-хинтинг с логикой.

P.S. Ничего вы так «посмотрели» 400+ страниц за полчаса.
ваш пример почти идеален =)

чтобы было понятнее, чуть уточню:
function createInvoice(Account $issuer, Account $billTo, $amount)


Ну надуманный пример, конечно, но иллюстрирует мою мысль.
Нужны ли «от кого» и «кому» — это уже специфика конкретной задачи. Мой пример может описывать систему, в которой все счета выставляются текущим сервисом, поэтому «кому» можно опустить.

Далее, не $issuer, а $issuerAccount, и не $billTo, а $receiverAccount. Ваше именование, по-моему, неоднозначно.
Честно говоря, так глубоко влазить в рассуждения я не планировал.

$billTo — это англоязычная терминология, имеющая отношение к инвойсам. Если вам хочется написать $billToAccount — ради бога, если ваша IDE не дает полного понимания, на объект какого типа вы смотрите в данный момент, или по иной причине.

В примере, который я привел в начале треда, вместо $billToAccount было бы написано $accountEntity.

Можем отвлеченно порассуждать на тему, нужно или нет указывать $issuer в методе в контексте stateless classes :-)
IDE понимание даёт. Но в вашем случае надо знать предметную область (на английском), чтобы быстро разбираться в коде. В моём случае — код будет понятен любому. По сути ведь можно ввести, что user1 — это всегда объекта, а user2 — это всегда субъект. Такая вот договорённость, но не каждый сразу поймёт.

В данном случае человеку придётся разбираться — billTo — это счёт того, кому придёт платёж или кому нужно оплачивать.

Вообще, не очень люблю, когда код переполнен бизнес-лексикой. Если есть вариант использовать общеупотребительные аналоги, стараюсь использовать их. В этом плане payeeAccount — счёт плательщика, а receiverAccount — счёт получателя. Насколько помню, при оплате на сайтах встречал эти термины. Гугление дало использование этих терминов, например, UPS.

Но я не настолько много работал в узких областях, так что это, скорее, специфика моего мироощущения.
Но в итоге мой (не надуманный, а вполне реализованный на этой неделе) метод выглядит так:
public function createInvoice(Account $payeeAccount, Account $receiverAccount, Currency $currency, $amount)
{
   ...
}
Этот код кардинально отличается от приведенного мной =)
Спасибо за дискуссию.
Да-да, я неправильно понял одну из ваших фраз.
Да, я вас неправильно понял. Я думал, вы про бизнес-логику в целом говорите, а не про конкретный пример, который вы описали. Ну, собственно, с этим я согласен. Как именно обработать и зачем нужны две этих сущности из именования непонятно.
Вот кстати про последнее: недавно видел код сайта бесплатных объявлений, где таблица объявлений называется tbl_objavlenia
При этом таблица с данными авторизации называется tbl_users
Хоть какого-нибудь стандарта/стиля придерживались бы, что ли…
Пфф
image
Сурово…
Ну, это, видимо, какая-то подработка одинэсника.
Наличие двух одинаковых таблиц еще простительно, наименования — вещь на любителя, но за varchar без n поубивал бы…
varchar(100)

Так что успокойтесь и положите топор на место.
Вы не поняли — речь идет не про длину, а про широкие символы.

varchar — это последовательность байт в хрен знает какой кодировке. Под линуксом уже научились использовать utf-8, но на скриншоте приведен виндовый клиент к виндовому же Microsoft Sql Server — а значит, ад с кодировками и локалями, системными и БД, гарантирован.

nvarchar — это последовательность байт в кодировке UTF-16 (или UCS-16). Пока не захочешь писать в базу данных иероглифы — никаких проблем с национальными символами не будет. Да и иероглифы с большой вероятностью успешно пройдут.
А в каких СУБД varchar может быть без размерности? С другой стороны, «безразмерный» varchar имеет длину, равную максимальной длине varchar'а в системе. Не думаю, что это хуже неким «256 символов». Почему 256, кто это посчитал, что это за класс строки такой, что ему нужно именно 256, а не 200, 140 или 70.

Тут вот, например, артикул в 100 символов. Есть ли разница, ограничен артикул 100 символами или дефолтом БД при условии, что varchar всё равно «резиновый»?
Ок, прошу прощения. Но зато ещё одну хорошую практику для себя отметил :)
НЛО прилетело и опубликовало эту надпись здесь
Борода и свитер стали постепенно мутировать в усики и китель?
НЛО прилетело и опубликовало эту надпись здесь
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации