О перегрузке объектов лишними сущностями
Очень часто я сталкиваюсь с подходом в программировании, который я мог бы описать как “все включено”.
Рассмотрим простой пример: некий разработчик игр, который пишет на С++, решил что STL это тормоз и вообще ужасная вещь, надо писать свой велосипед. Он сделал свои контейнеры, свои строки, все свое. Но рассказ будет не о NIH синдроме, а о том, как эти классы были спроектированы.
Возьмем его класс строки, как пример. Он умел все. Кроме базовых операций по работе со строками (сравнений и т.д.), строка умела читать себя из файла, писать себя в файл, конвертироваться во все базовые типы С++ и тому подобное. Все это помещалось в одном исходной файле размером этак под 160+ килобайт.
Это была присказка.
Так вот, на днях, один из разработчиков на нашем проекте (ASP.NET) решил сделать extension для списка строк, который бы умел сохранять и грузить его из CSV файла. В C# extension это что-то типа синтаксического сахара над существующим классом, который представляет собой функцию которую можно вызвать через объект данного класса. Для примера выше это было бы что-то типа такого:
List csv = List.LoadCSV("hello.csv");
csv.SaveCSV("hello2.csv");
Загрязнять объекты методами, которые не являются их неотъемлемой частью - это зло. Такой код потом тяжело поддерживать, классы раздуваются, теряется “идеологическая стройность” существующего кода, а так же теряется расширяемость - логика поведения таких вот утилитарных функций становится зашита в класс.
Думаете эти примеры придуманы и в реальном мире не встречаются?
Вот взять web2py, зачем делать экспорт и импорт данных в CSV частью объекта базы данных? Разве такой код сложнее написать:
export_to_csv(db, open('somefile.csv', 'wb'))
чем такой:
db.export_to_csv(open('somefile.csv', 'wb'))
Но во втором случае, идет жесткая привязка к типу объекта (база данных) и теряется reuse кода - duck typing в питоне никто не отменял.
Другой пример из того же web2py: сохранение результатов запроса как HTML! Это вообще нонсенс, включать в ORM логику представления:
rows = db(db.person.id > 0).select() print rows.xml()
Или вот взять Руби (за пример спасибо Александру Соловьеву):
piranha@gto ~> irb >> String.methods.length => 98 >> Fixnum.methods.length => 97 >> require 'active_support' => true >> String.methods.length => 190 >> Fixnum.methods.length => 188
Зачем?
Может кто-то знает почему такое делают и почему это может быть, даже теоретически, хорошо?