Це більш добре сформована транскрипція мого початкового коментаря під вашим запитанням. Відповіді на запитання ОР можна знайти внизу цієї відповіді. Також будь ласка, перевірте важливу примітку, що знаходиться там же.
Те, що ви зараз описуєте, Sipo, - це шаблон дизайну, який називається Активний запис . Як і у всьому, навіть цей знайшов своє місце серед програмістів, але був відкинутий на користь сховищ та моделей картографічних даних з однієї простої причини - масштабованості.
Коротше кажучи, активний запис - це об'єкт, який:
- представляє об’єкт у вашому домені (включає бізнес-правила, знає, як поводитися з певними операціями над об’єктом, наприклад, якщо ви можете чи не можете змінити ім’я користувача та інше),
- знає, як отримати, оновити, зберегти та видалити об'єкт.
Ви звертаєтесь до декількох питань із вашим поточним дизайном, і головна проблема вашого дизайну вирішена в останньому, шостому, пункті (я думаю, останнє, але не менш важливе). Коли у вас є клас, для якого ви проектуєте конструктор, і навіть не знаєте, що конструктор повинен робити, клас, ймовірно, робить щось не так. Це сталося у вашому випадку.
Але виправити дизайн насправді досить просто, розділивши представлення сутності та логіку CRUD на два (або більше) класи.
Ось як виглядає ваш дизайн зараз:
Employee
- містить інформацію про структуру працівника (її атрибути) та методи, як змінити сутність (якщо ви вирішили піти змінним шляхом), містить логіку CRUD для Employee
сутності, може повернути список Employee
об'єктів, приймає Employee
об'єкт, коли ви хочете оновити працівника, може повернути особу Employee
через такий методgetSingleById(id : string) : Employee
Нічого собі, клас здається величезним.
Це буде запропоноване рішення:
Employee
- містить інформацію про структуру працівника (його атрибути) та методи, як змінити сутність (якщо ви вирішили йти змінним шляхом)
EmployeeRepository
- містить логіку CRUD для Employee
сутності, може повернути список Employee
об'єктів, приймає Employee
об'єкт, коли ви хочете оновити працівника, може повернути одиночку Employee
за допомогою методу, якgetSingleById(id : string) : Employee
Ви чули про роз'єднання проблем ? Ні, ви зараз. Це менш сувора версія принципу єдиної відповідальності, яка говорить про те, що клас повинен насправді мати лише одну відповідальність, або, як каже дядько Боб:
Модуль повинен мати одну і єдину причину зміни.
Цілком зрозуміло, що якщо мені вдалося чітко розділити ваш початковий клас на два, які все ще мають добре закруглений інтерфейс, початковий клас, ймовірно, робив занадто багато, і це було.
Що чудово стосується шаблону репозиторію, він не тільки виступає як абстракція, щоб забезпечити середній рівень між базою даних (який може бути будь-яким, файловим, noSQL, SQL, об'єктно-орієнтованим), але він навіть не повинен бути конкретним клас. У багатьох мовах OO ви можете визначити інтерфейс як фактичний interface
(або клас із чисто віртуальним методом, якщо ви перебуваєте на C ++), а потім мати кілька реалізацій.
Це повністю знімає рішення про те, чи репозиторій є реальною реалізацією, ви просто покладаєтесь на інтерфейс, фактично покладаючись на структуру з interface
ключовим словом. А сховище саме це, це фантазійний термін для абстрагування рівня даних, а саме - зіставлення даних у ваш домен та навпаки.
Ще одна чудова річ, як розділити його на (принаймні) два класи - це те, що зараз Employee
клас може чітко керувати власними даними та робити це дуже добре, оскільки йому не потрібно піклуватися про інші складні речі.
Питання 6: Отже, що повинен робити конструктор у новоствореному Employee
класі? Це просто. Слід взяти аргументи, перевірити, чи вони дійсні (наприклад, вік, мабуть, не повинен бути негативним чи ім'я не повинно бути порожнім), створити помилку, коли дані були недійсними та якщо передача перевірки призначила аргументи приватним змінним суб'єкта господарювання. Тепер він не може спілкуватися з базою даних, тому що просто не має уявлення, як це зробити.
Питання 4: Не можна відповісти взагалі, не загалом, тому що відповідь сильно залежить від того, що саме вам потрібно.
Питання 5: Тепер, коли ви розділили роздутий клас на два, ви можете мати кілька методів оновлення безпосередньо на Employee
класі, як changeUsername
, наприклад markAsDeceased
, який буде маніпулювати даними Employee
класу лише в оперативній пам'яті, а потім ви можете ввести такий метод, як registerDirty
від Шаблон блоку роботи до класу сховища, за допомогою якого ви повідомляєте сховищу, що цей об’єкт змінив властивості і його потрібно буде оновити після виклику commit
методу.
Очевидно, що для оновлення об'єкт повинен мати ідентифікатор і таким чином вже бути збереженим, і це відповідальність сховища виявляє це і викликає помилку, коли критерії не виконуються.
Питання 3: Якщо ви вирішили перейти з моделлю одиниці роботи, create
тепер це буде метод registerNew
. Якщо ви цього не зробите, я б, мабуть, назвав це save
замість цього. Мета репозиторію - забезпечити абстракцію між доменом і рівнем даних, через це я рекомендував би вам, щоб цей метод (був він registerNew
чи save
) приймав Employee
об'єкт, і це залежить від класів, що реалізують інтерфейс сховища, який визначає атрибути вони вирішують вивезти суб'єкт господарювання. Передати цілий об'єкт краще, тому вам не потрібно мати багато необов'язкових параметрів.
Питання 2: Обидва способи тепер будуть частиною інтерфейсу сховища, і вони не порушують принципу єдиної відповідальності. Відповідальність репозиторію полягає в наданні операцій CRUD для Employee
об'єктів, тобто це робиться (окрім читання та видалення, CRUD перекладається як на створення, так і на оновлення). Очевидно, ви можете розділити сховище ще більше, маючи EmployeeUpdateRepository
та інше, але це рідко потрібно, і одна реалізація зазвичай може містити всі CRUD-операції.
Питання 1: Ви закінчили простий Employee
клас, у якого тепер (серед інших атрибутів) буде ідентифікатор. Заповнення чи порожній ідентифікатор (або null
) залежить від того, чи об'єкт уже збережений. Тим не менш, ідентичний номер все ще є атрибутом, яким володіє суб'єкт господарювання, і відповідальність Employee
суб'єкта господарювання полягає в тому, щоб дбати про свої атрибути, отже, піклуватися про свій ідентифікатор.
Незалежно від того, чи має суб'єкт господарювання чи не має його, зазвичай це не має значення, поки ви не спробуєте виконати певну послідовність. Як зазначено у відповіді на запитання 5, відповідальність за сховище виявляє, що ви не намагаєтесь зберегти сутність, яка вже збережена, або намагається оновити об'єкт без ідентифікатора.
Важлива примітка
Будь ласка, майте на увазі, що хоча розділення проблем є великим, насправді проектування функціонального шару сховища є досить копіткою роботою, і на мій досвід, трохи складніше отримати правильний підхід, ніж підхід до активного запису. Але ви закінчите дизайн, який є більш гнучким і масштабованим, що може бути хорошою справою.
Employee
об'єкт для надання абстракції, питання 4. і 5., як правило, невідповідні, залежать від ваших потреб, і якщо ви розділите структуру та операції CRUD на два класи, то цілком зрозуміло, що конструкторEmployee
не може отримати дані від db більше, так що відповіді 6.