Що альтернативно для Singleton


114

У нас є клас, який містить інформацію про конфігурацію програми. Це раніше було одинаком. Після деякого архітектурного огляду нам сказали зняти синглет. Ми побачили певні переваги не використовувати одиночне тестування, оскільки ми можемо протестувати різні конфігурації відразу.

Без сингтона нам належить передавати екземпляр скрізь у нашому коді. Стає так безладно, тому ми написали обгортку з однотонним покриттям. Тепер ми передаємо один і той же код до PHP та .NET, мені цікаво, чи є краща модель, яку ми можемо використовувати для об’єкта конфігурації.

Відповіді:


131

У блозі Google Testing є ряд записів про те, щоб уникнути Singleton (щоб створити тестовий код). Можливо, це може вам допомогти:

Остання стаття докладно пояснює, як перемістити створення нових об'єктів на завод, щоб уникнути використання одиночних клавіш. Варто прочитати точно.

Коротше кажучи, ми переміщуємо всіх нових операторів на завод. Ми об'єднуємо всі об'єкти подібного терміну експлуатації на одну фабрику.


3
*** Використання ін'єкційних наркотиків для уникнення синглів
Джастін

Ці статті настільки ж хороші, як і стандарти програмування Google C ++!

2
Ну не дуже. Порада "Не використовувати статичні методи" відповідає, наприклад, принципу мінімального інтерфейсу Скотта Майєра / Герба Саттера. Є корисні поради, але їм не вистачає вкладів багатьох розумів.
Матьє М.

@FrankS, чому ви переключили порядок посилань? Спочатку це було в хорошому хронологічному плані.
Крегокс

@Cawas насправді не мав ідеї, це було більше чотирьох років тому, тому, мабуть, у мене тоді були певні причини :-)
FrankS

15

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

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


1
Я не згоден із твердженням про "найкращий спосіб", але +1 - хороша альтернатива.
tylermac

Проблема такого підходу полягає в тому, що кожен новий об'єкт містить (або посилання) на те, що потенційно може бути величезною кількістю даних. var_dump () на будь-якому з цих об'єктів, що містять гоб, призводить до того, що у величезних списках вільно з'являються запити про рекурсії . Це потворно некрасиво, не може бути надзвичайно ефективним, і робить речі здаються непростими. Однак я краще не знайшов кращого способу. Я перетворив "заводський" метод на використання __construct () для посилання на глобальний. Схоже, все
нахилилося

2
@EastGhostCom: ми можемо також використовувати сингл і перестати намагатися ускладнити для себе речі :)
gbjbaanb

5

Я можу зазначити очевидне тут, але чи є причина, чому ви не можете використовувати рамки введення залежності, такі як Spring або Guice ? (Я вважаю, що Весна також доступна для .NET, як і зараз).

Таким чином, рамка може містити єдину копію об'єктів конфігурації, і ваші боби (сервіси, DAO, що завгодно) не повинні турбуватися про пошук.

Такий підхід я зазвичай беру!


4

Якщо ви використовуєте Spring Framework , ви можете просто створити звичайний боб. За замовчуванням (або якщо ви явно встановлені scope="singleton") створюється лише один екземпляр bean, і цей екземпляр повертається кожен раз, коли боб використовується в залежності або отримується через getBean().

Ви отримуєте перевагу одного екземпляра без з’єднання шаблону Singleton.


3
О, іронія - використовуйте (сингл) весняні боби, щоб замінити сингл ...
Зак Макомбер

4

Альтернатива - це передача того, що вам потрібно, а не запитування предмета про речі.


4

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

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

Спробуйте рефакторинг коду, щоб уникнути загального, глобального та великого Configurationоб'єкта. Передати лише необхідні параметри класам клієнтів:

class Server {

    int port;

    Server(Configuration config) {
        this.port = config.getServerPort();
    } 

}

має бути відновлено для:

 class Server {

    public Server(int port) {
       this.port = port;
    }
 }

рамки ін'єкції залежностей допоможе багато тут, але це не stricly потрібно.


Так, це дійсно вдало. Я робив це раніше. Мій великий об'єкт конфігурації реалізовував інтерфейси, такі як MailServiceConf, ServerConf .., ніж Dependency Injection Framework передає конфігурацію класам, тому мої класи не залежали від великого об'єкта конфігурації.
caltuntas

1

Ви можете виконати ту саму поведінку одиночного, використовуючи статичні методи. Стів Йегге дуже добре пояснює це у цій публікації.


Насправді стаття є досить хорошою, і вона не говорить про те, що слід використовувати замість неї статичні методи. Натомість він заявляє, що статичні методи - це також одинаки, і, наприкінці, він рекомендує використовувати заводський візерунок методу: "Я закрию, кажу, що якщо ви все ще відчуваєте необхідність використовувати об'єкти Singleton, подумайте про те, щоб замість цього використати шаблон заводських методів. ... »
FrankS

0

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


1
Якщо клас без громадянства, це повинен бути статичний клас.
AlbertoPL

1
Він знаходиться в C ++ - візерунок відомий як Monostate.

0

Залежить від того, які інструменти / рамки тощо використовуються. За допомогою інструментів ін'єкцій залежностей / йоків часто все ще можна отримати одиночну продуктивність / оптимізацію, використовуючи контейнер di / ioc, використовуючи одиночну поведінку для потрібного класу - (наприклад, інтерфейс IConfigSettings), створюючи лише один екземпляр класу. Це все ще може бути замінено для тестування

Крім того, можна створити фабрику для створення класу та повернути один і той же екземпляр кожного разу, коли ви його запитуєте, але для тестування він може повернути стерту / знущену версію


0

Перегляньте можливість зробити конфігурацію як інтерфейс зворотного дзвінка. Таким чином, ваш конфігураційний код буде виглядати:

MyReuseCode.Configure(IConfiguration)

Код системи-init буде виглядати:

Library.init(MyIConfigurationImpl)

0

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


0

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

public static Singleton getInstance() {
    if(singleton != null)
        createSingleton();
        return singleton;
    }
}

Ви можете зателефонувати createSingleton(Information info)безпосередньо при запуску програми (і в setUp-Методи одиничних тестів).


-1

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

DI з використанням весни тощо - це дуже хороший варіант, але не єдиний варіант.

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