Уникайте методу ініціалізації


12

У мене є цей існуючий код, де у них є клас та метод ініціалізації в цьому класі. Очікується, що як тільки об’єкт класу буде створений, їм потрібно викликати ініціалізацію на ньому.

Причина, чому існує метод ініціалізації . Об'єкт створюється рано, щоб мати глобальну область застосування, а потім метод ініціалізації викликається пізніше після завантаження dll, від якого залежить.

Проблема з ініціалізацією Клас тепер має цей boIl ініціалізований, який потрібно перевірити в кожному методі, перш ніж він продовжується і повертає помилку, якщо вона не ініціалізована. Простіше кажучи, це великий біль.

Одне можливе рішення Ініціалізувати в конструкторі. Майте лише вказівник на об’єкт у глобальній області. Створіть фактичний об’єкт після завантаження dll.

Проблема з вищевказаним рішенням Кожен, хто створює об'єкт цього класу, повинен знати, що його потрібно створити лише після завантаження dll, інакше він вийде з ладу.

Це прийнятно?


У мене була ця сама проблема під час створення об’єктів, пов’язаних з OpenGL, які потрібно екземплярувати до існування контексту OpenGL, але вони повинні містити об'єкти, що залежать від OpenGL, такі як записки, текстури тощо.

3
Дурне питання №1: Чому конструктор об'єктів не може завантажити DLL, якщо він ще не завантажений?
Джон Р. Стром

1
Вибачтесь за те, що ми відновили старе запитання, але у випадку, якщо хтось прочитає це у 2017 році, використовуйте call_onceв C ++ 11 . Проекти, яких ще немає на C ++ 11, повинні вивчити, як реалізується call_once в C ++ 11 (зосередитись на тому, яку проблему вона вирішує, а потім як), а потім повторно реалізувати її у своєму (усталеному) смаку C ++. Він потребує багатопотокового безпечного примітиву синхронізації, стан якого необхідно статично ініціалізувати (з постійним значенням). Зауважте, що компілятори pre-C ++ 11 можуть мати інші ідіосинкразії, які потрібно задовольнити.
rwong

Відповіді:


7

Звучить як робота для віртуального проксі.

Ви можете створити віртуальний проксі, що містить посилання на відповідний об'єкт. Хоча DLL не завантажена, проксі може пропонувати клієнтам певну поведінку за замовчуванням, після завантаження DLL проксі просто пересилає всі запити на реальну тему.

Віртуальний проксі відповідає за перевірку ініціалізації DLL і на підставі цього вирішує, чи потрібно делегувати запит реальній темі.

Шаблон проксі у Вікіпедії

Як вам подобається ця ідея?


Ви дійсно багато не вирішуєте тут. для кожного методу, доданого до фактичного об'єкта, вам також потрібно буде додати цей метод до проксі. Немає?

Це дозволяє уникнути проблеми запам'ятовування виклику init. функція, але здається, що кожну функцію проксі ще потрібно перевірити, чи завантажено .dll.

1
@Sriram зараз велика різниця, використовуючи проксі, ви обмежили технічне обслуговування, пов’язане з перевіркою DLL, до одного класу: проксі. Клієнтам класу взагалі не потрібно знати про перевірку DLL. Оскільки більшість методів буде робити делегування, я не бачу великої проблеми в тому, щоб реалізувати інтерфейс як у реальній темі проксі. Іншими словами, ви можете запобігти створенню реальної тематики, поки не будете впевнені, що DLL завантажена, і тоді вам не потрібно буде кожен раз перевіряти стан DLL.

@edalorzo: хоча ідея хороша, питання тут полягає в тому, що ми вже говоримо про проксі, і ОП скаржиться на необхідність перевірки в кожному методі його проксі ...
Матьє М. М.

4

Нічого корисного не відбувається, якщо DLL ще не завантажена; об’єкт генерує лише помилку. Це фатальна помилка? Як обробляється помилка?

Чи гарантує ваша методологія тестування, щоб ця помилка ніколи не виникала у готовому продукті?

Це звучить як робота для тестування, а не для архітектурного дизайну. Не повертайте помилку, assertщоб вона ніколи не траплялася.

Виправте будь-яких клієнтів класу, які в даний час вловлюють та ігнорують помилку, щоб не намагатися спричинити її в першу чергу.

Багатопоточним шаблоном може бути встановлення змінної загальної умови після завантаження DLL і змушення конструктора об'єкта чекати (і блокувати інші потоки) до завантаження DLL.


Я думаю, що ви праві, немає нічого поганого в тому, щоб викинути виняток при створенні відповідного об'єкта, якщо DLL не була належним чином ініціалізована. Клієнтський код тоді повинен мати справу лише з цим винятком, і тестування повинно забезпечити належне поводження з виключенням.

2

Перше: уникайте глобальних об’єктів як шкідника, якщо їх стан ніколи не змінюється (конфігурація).

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

Я придумав дві ідеї:

  1. Використовуйте Фасад для завантаження DLL. Доступ до Об'єкту можна отримати лише через Фасад, Фасад завантажує DLL при створенні та одночасно інстанціює Об'єкт.

  2. Використовуйте проксі, але розумний вид;)

Дозвольте детальніше зупинитися на другому питанні, оскільки я боюся, що відповідь @edalorzo може вас налякати:

// Private
static Object& GetObjectImpl() { static Object O; return O; }

// Public
static Object& GetObject() {
  Object& o = GetObjectImpl();
  assert(o.isInitialized() && "Object not initialized yet!");
  return o;
}

Тепер у вас є лише один чек.

Це також можна зробити за допомогою якогось розумного вказівника:

Pointer<Object> o;

Тут ви залишаєте лише місце для вказівника, спочатку нульового, і лише коли завантажується DLL, ви фактично розподілите об'єкт. Усі попередні звернення повинні створювати виняток (NullException: p?) Або припиняти програму (чисто).


1

Це складне питання. Я думаю, що архітектура може допомогти у вирішенні проблеми.

З доказів це здається, що .dll не завантажується під час завантаження програми. Це майже звучить як плагін.

Одне, що спадає на думку, це те, що вам потрібно ініціалізувати .dll. Програміст повинен припустити, що об'єкт не повернеться надійно, в будь-якому випадку. Можливо, ви зможете скористатись цим ( bool isObjectLoaded() { return isInitialized; }), але ви обов'язково отримаєте більше звітів про помилки під час чеків.

Я думав про одиноку.

Якщо ви зателефонували в одиночний, він повинен повернути правильний об'єкт, якщо він може його отримати. Якщо він не може повернути правильний об'єкт, ви повинні отримати деяке значення помилки / nullptr / порожній базовий об'єкт. Це не спрацює, якщо об’єкт може загинути від вас, хоча.

Крім того, якщо вам потрібні кілька примірників об'єкта, ви можете використовувати щось на зразок Factory.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.