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

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

>Я по прежнему считаю, что NoSQL — это слишком молодые продукты

NoSQL исторически появился раньше SQL-а, собственно весь ынтырпрайз до 70-х им в жопу и долбился. Потом британский ученый изобрёл теорию РБД, появление которой привело к немедленному выметанию всего этого ёбаного хаоса с рынка, стандартизации и тотальному овладиванию SQL-а в рекордно короткие сроки. Побочным эффектом стало то, что всякое быдло начало пихать SQL туда, где он не очень-то и нужен, и очень, блядь, страдать, от того, что их гостевухи стали долго загружаться. Потом кто-то сделал фундаментальное открытие — оказывается хранить профили пользователей гостевухи в хешь-таблице в памяти и доставать их оттуда по имени гораздо быстрее, чем реализовывать EAV поверх РБД. После этого переворота в мозгах гостевушников они приняли радостно сверкать новым базвордом по своим блогам и хабро-хабрикам, радуясь, что им в очередной раз удалось повернуть стрелку прогресса вспять и укусить себя за жопу.
Может оно и так, но тут речь о другом.
О том что адекватное проектирование приложения — адекватно? Капитанство, как по мне. Поэтому я и не стал комментировать основную статью, просто указал на заблуждения.
Ну, я рад, что капитанство… надеюсь для всех.

Что же, касается «заблуждений». То вы несомненно правы в «пихать SQL туда, где он не очень-то и нужен», но это не отменяет и обратного «пихать NoSQL туда, где оно не пригодно».
Хотя, вот вы говорите капитанство, но кто-то же пишет .dll к базам данных, не ужели им слабо сделать нормальный интерфейс, чтобы мне не приходилось накручивать сверху отображение обобщенных методов?
>кто-то же пишет
Ну а вы не пишите. В чем проблема? Кто-то и не такое пишет, что же теперь, за ними повторять?
> что же теперь, за ними повторять?

Нет, конечно. Но вот и получаем, что «капитанство не для всех»
В частности MongoDB: Initial release 2009
Спасибо за ваш комментарий. Поднял настрой и боевой дух аж))
Не проще ли сделать интерфейс Storage, который будут реализовывать классы типа MySqlStorage, MongoDbStorage, FileSystemStorage, SharedMemoryStorage, SomeCloudStorage и т. п. и запихивать его куда надо с помощью DI?
Это практически тоже самое :)
Впрочем, смотря что имеется введу. Речь о interface IStorage? Тогда сам MongoDB не может его реализовывать (не мы его создаем, а производитель), а делать еще лишний класс MongoDbStorage — не проще. Иерархия тут подойдет лучше, т.к. при адаптации есть много общего.
Вы говорите «когда понадобится работать с другими базами, в том числе с реляционными, это легко изменится путем настройки класса Database», а не об иерархии. Меняя хранилище вам придётся менять код класса Database. А если понадобится два хранилища одновременно? Про возможность задать тип хранилища в конфиге я вообще молчу.
Я писал и про иерархию так же

> для этого должен быть выделен специальных класс (или их иерархия…

Меняя хранилище вам же сравнительно придётся создавать новое хранилище, например, MySqlStorage.

Мне или поменять сам Database, или создать от него наследника MySqlDatabase и в нем прописать отличия.

В итоге разницы практически нет, но у вас нет места для кода одинакового для всех хранилищ.
Предполагаю, что VolCh намекал Вам на создание интерфейса хранилища IStorage, далее создать конкретные реализации этого хранилища (MySqlStorage, MongoDbStorage и т.д.), потом необходимое забиндить:
ninjectKernel.Bind<IStorage>().To<MySqlStorage>();

и везде работать с IStorage.
Когда базу нужно будет сменить, просто измените бинд на требуемый:
ninjectKernel.Bind<IStorage>().To<MongoDbStorage>();


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

Если будет общий код во всех хранилищах, то можно создать абстрактный класс для него. Если для некоторой подгруппы (скажем код для разных SQL БД будет наверняка похож, а вот с кодом для файлового или веб-хранилища очень мало общего будет) — то абстрактный класс для неё.
Да, именно так.
>А если понадобится два хранилища одновременно?

Будет два объекта Database, или же их наследники (зависит от разницы между базами данных, возможно достаточно иерархически выделять только объектные отдельно, реляционные отдельно)
> Про возможность задать тип хранилища в конфиге я вообще молчу

Тут тем более ни каких проблем, читаем и создаем объекты от Database с нужными параметрами
Можно для вашего класса в конфиге поменять Mongo на RPC-сервис прозрачно? )
Идея, конечно, интересная, но разве не проще сделать следующие сигнатуры?

Database… public void Save(T obj) { db.GetCollection… }

и вызов

new Database().Save(myObj), где тип определяется без всякой магии
… съелось. хотел написать

public void Save<T>(T obj)...
Я так понял вы предлагаете

public void Save2<T>(T argObject)
{
  var collection = db.GetCollection<T>();
  collection.Save(argObject);
}
 


Не работает первая строка — ошибка «Для использования в качестве параметра „T“ в универсальном типе или методе „MongoDB.IMongoDatabase.GetCollection()“ тип „T“ должен быть ссылочным типом»

Честно говоря не знаю что это значит
спасибо, именно это. для ссылочного типа нужно дописать ограничение:

public void Save<T>(T argObject)
    where T : class
{
  var collection = db.GetCollection<T>();
  collection.Save(argObject);
}
Действительно, спасибо за идею. Век живи, век учись :)
Совершенный код*? Вы серьёзно?
Ну так — Save2, myType, pi, m %)
Ну, вы меня просто смешите — это названия из msdn, учите жену именовать переменные
вы забыли ещё локальные переменные с большой буквы :-)
Вполне
Вы пишете о том, что данный подход вы использовали чтобы а) легко заменить хранилище при необходимости
б) использовать в коде универсальный метод.
Так у меня вопросы:
а) как часто после проектирования арихтектуры вы внезапно меняете хранилище? Мне это еще ни разу не приходилось. Излишняя оптимизация по Кнуту…
б) А как вы получаете объекты? Не увидел у вас никаких суперуниверсальных методов для извлечения данных из хринилища, а это значит, что вы все равно используете нативные методы вашего MongoDB и при смнене хранилища по пункту а) вам все равно придется перелапачивать код.
Если уж вы начали обсуждать данную тему, тогда реализуйте полностью паттерн Repository.
Вы это серьезно? Не приходилось? Так как только, найдется способ использовать Oracle NoSQL Database, я тут же перейду на него. А если объемы вырастут, я буду серьезно подумывать о переходе на реляционные базы.
Что касается реализации паттерна, то я вам предложу альтернативу.

Должен быть базовый класс, от которого наследуются все классы, которые будем сохранять в базе, пусть это будет DBData. Тогда у него будут просто 3 метода: Load, Save, Delete. А обращение к базе уже будет делом DBData. Вот и все. Человек наследуется от DBData, и затем просто сохраняет или подгружает свои объекты.
Вы просто убили принцип «единственной ответсвенности» — сущность ничего не должна знать о базе данных или прочем хранилище, где она будет хранится.
Нет как раз, осуществил принцип инкапсуляции, сущность должна уметь сохраняться и подгружаться. И конечно, она не знает деталей реализации базы, но пользоваться ей вполне может прозрачно для пользователя сущности.

А иначе вы нагружаете пользователя базы не нужным для него знанием — выбери базу, а затем сохрани — это не его дело. Уговорили, чуть позже продемонстрирую полное решение в коде.
Принцип инкапсуляции говорт о том, что сущность должна уметь менять свое внутреннее состояние, но никак не сохранение в базу данных.
Пользователь базы данных — это некая прослойка между базой и сущностями, тот же паттерн Repository или Table — они знают все о базе и знают о типе сущностей и ничего больше. Именно на этом уровне возможна смена базы данных.
А то что делаете вы — это просто большая связность кода, которая приведет к издержкам поддеркжи.

Например, в DBData вы что будете создавать экземляр коннекшина к базе? А если у вас будет тысячи, десятки тысяч загруженных в память сущностей? Или вы будете создавать singleton экземляр базы даных? Я вижу в любом случае сложноподдерживаемый, слаботестируемый код.

Есть класс Ученик и есть класс Группа, состоящая из списка учеников. Вы создаете новую группу, добавляете в нее учеников. Как вы будете сохранять все эти взаимосвязанные струтуры? А если у вас один из учеников вызовит исключение, как вы его обработаете должным-транзакционным образом в вашем случае, если каждый объект независимо отвественнен за самосохранение в базу данных? И много-много других случаев. Не зря умные люди придумали все эти паттерны, а то что делаете вы — это уровень лабораторных работ с 80-ти летним преоподавателем церковно-приходской сельской школы.
Почитайте GoF, Руководство Microsoft по проектированию архитектуры приложений. 2е издание
DBData будет получать нечто похожие на singleton экземляр базы даных. Не расходитесь к вечеру выложу решение. У меня сейчас КВН :)
Ряд этих паттернов обладают существенными недостатками, и непонимание этого ведет к проблемам.
В этом отношении я полностью с вам согласен, что слепое применение паттернов, да еще не дай бог одновременно конкурирующих или протипостовляющих не приведет ни к чему хорошему, но то что пытаетесь сделать вы — просто нарушение здравого смысла.
Давайте Вы подождете со своими выводами, или хотя бы разберетесь с тем, что я делаю?
Извините, я умываю руки. Вы смешали чуть ли не все bad practice в одном месте. Вразумить не получится ни у меня ни у кого либо еще. Читайте внимательно комментарии к вашей очередной статье.
Я думаю, вы и ряд колегг мыслящих также — существенно заблуждаются, но так как аргументированно дискутировать вы не желаете, то увы помочь вам ничем не могу.
Поздравляю вас, вы изобрели паттерн Repository.
Нет, я сделал нечто лучшие
«Ответ аргументируйте», как любят писать в задачах. Мне интересно, каковы вообще существенные отличия того, что вы тут предлагаете, от типового репозитория; я вот что-то их не вижу.
Вот реализация паттерна Репозитарий — об этом речь. Если да, то я не понимаю как вы не видите отличий. Если нет, то дайте ссылку на конкретную реализацию, чтобы можно было понять о чем вы.
Вот так и не вижу. Репозиторий — это паттерн, при котором некоторому объекту вменяется в обязанность сохранение/загрузка объектов. Ваш Database выполняет именно эти обязанности (в урезанном виде, потому что Load вы не реализовали).

martinfowler.com/eaaCatalog/repository.html
Видите ли «паттерн, при котором некоторому объекту вменяется в обязанность сохранение/загрузка объектов» — это лирика, паттерн имеет конкретную реализацию. А вы тут виляете хвостом.
Вы что-то путаете. Паттерн на то и паттерн, чтобы иметь обобщенное описание и множество различных реализаций, с разными достоинствами и недостатками.

Вы Фаулера по ссылке прочитали? А оригинальную книжку?
Это вы похоже принципы спутали с паттернами. Мне не интересно говорить о абстрактных принципах, называемых вами паттернами… когда будите говорить о конкретной реализации паттерна, тогда и будет о чем говорить. А так вы постоянно выкручиваться будите…
«Это вы похоже принципы спутали с паттернами.»
Я ничего не спутал
«In software engineering, a design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. A design pattern is not a finished design that can be transformed directly into code. It is a description or template for how to solve a problem that can be used in many different situations. So patterns are formalized best practices that you must implement yourself in your application.» (вики)

«когда будите говорить о конкретной реализации паттерна, тогда и будет о чем говорить.»
Ну вот вам конкретная реализация: у нас в проекте есть Repository{of T}, имеющие методы T Load(Guid id) (у нас объекты имеют гарантированный идентификатор, но это легко генерализуется), IEnumerable{of T} Load(QueryObject filter) и Save(T data). Ничего не напоминает?

Причем мы достаточно рано выделили из него интерфейс, в основном для юнит-тестирования, а сейчас у нас под этим интерфейсом две принципиально разные реализации, одна на базе EntityFramework, и еще одна (на самом деле, тоже две, более старая и более новая) — на базе пропьетарного DAL.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории