Skip to content

Commit 6eb7a8c

Browse files
committed
Singleton improvements.
1 parent b34fe4f commit 6eb7a8c

File tree

5 files changed

+112
-52
lines changed

5 files changed

+112
-52
lines changed

RefactoringGuru.DesignPatterns.sln

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prototype.Conceptual", "Pro
4141
EndProject
4242
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proxy.Conceptual", "Proxy.Conceptual\Proxy.Conceptual.csproj", "{422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}"
4343
EndProject
44-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Singleton.Conceptual", "Singleton.Conceptual\Singleton.Conceptual.csproj", "{7ECFE5B9-B611-47FD-ABC4-7C648F599B75}"
45-
EndProject
4644
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "State.Conceptual", "State.Conceptual\State.Conceptual.csproj", "{111A1AE4-043C-40F5-915F-FC1CD903F751}"
4745
EndProject
4846
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Strategy.Conceptual", "Strategy.Conceptual\Strategy.Conceptual.csproj", "{AABC9303-B16F-4803-B5B4-8B7336EA96E8}"
@@ -51,6 +49,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplateMethod.Conceptual",
5149
EndProject
5250
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Visitor.Conceptual", "Visitor.Conceptual\Visitor.Conceptual.csproj", "{6834BA82-2BAA-485E-BB8F-96078DAC6877}"
5351
EndProject
52+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Singleton.Conceptual.NonThreadSafe", "Singleton.Conceptual\NonThreadSafe\Singleton.Conceptual.NonThreadSafe.csproj", "{4F3A1732-04E7-494E-A7BE-7764C21483B8}"
53+
EndProject
54+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Singleton.Conceptual.ThreadSafe", "Singleton.Conceptual\ThreadSafe\Singleton.Conceptual.ThreadSafe.csproj", "{4FBA49E6-3CBE-421D-8E4F-182F13B6319D}"
55+
EndProject
5456
Global
5557
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5658
Debug|Any CPU = Debug|Any CPU
@@ -125,10 +127,6 @@ Global
125127
{422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}.Debug|Any CPU.Build.0 = Debug|Any CPU
126128
{422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}.Release|Any CPU.ActiveCfg = Release|Any CPU
127129
{422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}.Release|Any CPU.Build.0 = Release|Any CPU
128-
{7ECFE5B9-B611-47FD-ABC4-7C648F599B75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
129-
{7ECFE5B9-B611-47FD-ABC4-7C648F599B75}.Debug|Any CPU.Build.0 = Debug|Any CPU
130-
{7ECFE5B9-B611-47FD-ABC4-7C648F599B75}.Release|Any CPU.ActiveCfg = Release|Any CPU
131-
{7ECFE5B9-B611-47FD-ABC4-7C648F599B75}.Release|Any CPU.Build.0 = Release|Any CPU
132130
{111A1AE4-043C-40F5-915F-FC1CD903F751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
133131
{111A1AE4-043C-40F5-915F-FC1CD903F751}.Debug|Any CPU.Build.0 = Debug|Any CPU
134132
{111A1AE4-043C-40F5-915F-FC1CD903F751}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -145,6 +143,14 @@ Global
145143
{6834BA82-2BAA-485E-BB8F-96078DAC6877}.Debug|Any CPU.Build.0 = Debug|Any CPU
146144
{6834BA82-2BAA-485E-BB8F-96078DAC6877}.Release|Any CPU.ActiveCfg = Release|Any CPU
147145
{6834BA82-2BAA-485E-BB8F-96078DAC6877}.Release|Any CPU.Build.0 = Release|Any CPU
146+
{4F3A1732-04E7-494E-A7BE-7764C21483B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
147+
{4F3A1732-04E7-494E-A7BE-7764C21483B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
148+
{4F3A1732-04E7-494E-A7BE-7764C21483B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
149+
{4F3A1732-04E7-494E-A7BE-7764C21483B8}.Release|Any CPU.Build.0 = Release|Any CPU
150+
{4FBA49E6-3CBE-421D-8E4F-182F13B6319D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
151+
{4FBA49E6-3CBE-421D-8E4F-182F13B6319D}.Debug|Any CPU.Build.0 = Debug|Any CPU
152+
{4FBA49E6-3CBE-421D-8E4F-182F13B6319D}.Release|Any CPU.ActiveCfg = Release|Any CPU
153+
{4FBA49E6-3CBE-421D-8E4F-182F13B6319D}.Release|Any CPU.Build.0 = Release|Any CPU
148154
EndGlobalSection
149155
GlobalSection(NestedProjects) = preSolution
150156
EndGlobalSection

Singleton.Conceptual/NonThreadSafe/Program.cs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,43 @@
1212

1313
namespace Singleton
1414
{
15-
// EN: The Singleton class defines the `getInstance` method that lets
16-
// clients access the unique singleton instance.
15+
// EN: The Singleton class defines the `GetInstance` method that serves as
16+
// an alternative to constructor and lets clients access the same instance
17+
// of this class over and over.
1718
//
18-
// RU: Класс Одиночка предоставляет метод getInstance, который позволяет
19-
// клиентам получить доступ к уникальному экземпляру одиночки.
19+
// RU: Класс Одиночка предоставляет метод `GetInstance`, который ведёт себя
20+
// как альтернативный конструктор и позволяет клиентам получать один и тот
21+
// же экземпляр класса при каждом вызове.
2022
class Singleton
2123
{
22-
private static Singleton _instance;
23-
24-
private Singleton()
25-
{ }
26-
27-
// EN: The static method that controls the access to the singleton
28-
// instance.
24+
// EN: The Singleton's constructor should always be private to prevent
25+
// direct construction calls with the `new` operator.
2926
//
30-
// This implementation let you subclass the Singleton class while
31-
// keeping just one instance of each subclass around.
27+
// RU: Конструктор Одиночки всегда должен быть скрытым, чтобы
28+
// предотвратить создание объекта через оператор new.
29+
private Singleton() { }
30+
31+
// EN: The Singleton's instance is stored in a static field. There there
32+
// are multiple ways to initialize this field, all of them have various
33+
// pros and cons. In this example we'll show the simplest of these ways,
34+
// which, however, doesn't work really well in multithreaded program.
3235
//
33-
// RU: Статический метод, управляющий доступом к экземпляру одиночки.
36+
// RU: Объект одиночки храниться в статичном поле класса. Существует
37+
// несколько способов инициализировать это поле, и все они имеют разные
38+
// достоинства и недостатки. В этом примере мы рассмотрим простейший из
39+
// них, недостатком которого является полная неспособность правильно
40+
// работать в многопоточной среде.
41+
private static Singleton _instance;
42+
43+
// EN: This is the static method that controls the access to the
44+
// singleton instance. On the first run, it creates a singleton object
45+
// and places it into the static field. On subsequent runs, it returns
46+
// the client existing object stored in the static field.
3447
//
35-
// Эта реализация позволяет вам расширять класс Одиночки, сохраняя
36-
// повсюду только один экземпляр каждого подкласса.
48+
// RU: Это статический метод, управляющий доступом к экземпляру
49+
// одиночки. При первом запуске, он создаёт экземпляр одиночки и
50+
// помещает его в статическое поле. При последующих запусках, он
51+
// возвращает клиенту объект, хранящийся в статическом поле.
3752
public static Singleton GetInstance()
3853
{
3954
if (_instance == null)
@@ -42,6 +57,16 @@ public static Singleton GetInstance()
4257
}
4358
return _instance;
4459
}
60+
61+
// EN: Finally, any singleton should define some business logic, which
62+
// can be executed on its instance.
63+
//
64+
// RU: Наконец, любой одиночка должен содержать некоторую бизнес-логику,
65+
// которая может быть выполнена на его экземпляре.
66+
public static void someBusinessLogic()
67+
{
68+
// ...
69+
}
4570
}
4671

4772
class Program

Singleton.Conceptual/ThreadSafe/Program.cs

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,50 +13,79 @@
1313

1414
namespace Singleton
1515
{
16-
// EN: The Singleton class defines the `getInstance` method that lets
17-
// clients access the unique singleton instance.
16+
// EN: This Singleton implementation is called "double check lock". It is
17+
// safe in multithreaded environment and provides lazy initialization for
18+
// the Singleton object.
1819
//
19-
// RU: Класс Одиночка предоставляет метод getInstance, который позволяет
20-
// клиентам получить доступ к уникальному экземпляру одиночки.
20+
// RU: Эта реализация Одиночки называется "блокировка с двойной проверкой"
21+
// (double check lock). Она безопасна в многопоточной среде, а также
22+
// позволяет отложенную инициализацию объекта Одиночки.
2123
class Singleton
2224
{
25+
private Singleton() { }
26+
2327
private static Singleton _instance;
2428

29+
// EN: We now have a lock object that will be used to synchronize
30+
// threads during first access to the Singleton.
31+
//
32+
// RU: У нас теперь есть объект-блокировка для синхронизации потоков во
33+
// время первого доступа к Одиночке.
2534
private static readonly object _lock = new object();
26-
27-
private string _value;
2835

29-
public Singleton(string value)
30-
{
31-
this._value = value;
32-
}
33-
34-
public string GetValue()
35-
{
36-
return this._value;
37-
}
38-
39-
// EN: The static method that controls the access to the singleton
40-
// instance.
41-
//
42-
// This implementation let you subclass the Singleton class while
43-
// keeping just one instance of each subclass around.
44-
//
45-
// RU: Статический метод, управляющий доступом к экземпляру одиночки.
46-
//
47-
// Эта реализация позволяет вам расширять класс Одиночки, сохраняя
48-
// повсюду только один экземпляр каждого подкласса.
4936
public static Singleton GetInstance(string value)
5037
{
51-
lock (_lock)
38+
// EN: This conditional is needed to prevent threads stumbling over
39+
// the lock once the instance is ready.
40+
//
41+
// RU: Это условие нужно для того, чтобы не стопорить потоки
42+
// блокировкой после того как объект-одиночка уже создан.
43+
if (_instance == null)
5244
{
53-
if (_instance == null)
45+
// EN: Now, imagine that the program has just been launched.
46+
// Since there's no Singleton instance yet, multiple threads can
47+
// simultaneously pass the previous conditional and reach this
48+
// point almost at the same time. The first of them will acquire
49+
// lock and will proceed further, while the rest will wait here.
50+
//
51+
// RU: Теперь представьте, что программа была только-только
52+
// запущена. Объекта-одиночки ещё никто не создавал, поэтому
53+
// несколько потоков вполне могли одновременно пройти через
54+
// предыдущее условие и достигнуть блокировки. Самый быстрый
55+
// поток поставит блокировку и двинется внутрь секции, пока
56+
// другие будут здесь его ожидать.
57+
lock (_lock)
5458
{
55-
_instance = new Singleton(value);
59+
// EN: The first thread to acquire the lock, reaches this
60+
// conditional, goes inside and creates the Singleton
61+
// instance. Once it leaves the lock block, a thread that
62+
// might have been waiting for the lock release may then
63+
// enter this section. But since the Singleton field is
64+
// already initialized, the thread won't create a new
65+
// object.
66+
//
67+
// Первый поток достигает этого условия и проходит внутрь,
68+
// создавая объект-одиночку. Как только этот поток покинет
69+
// секцию и освободит блокировку, следующий поток может
70+
// снова установить блокировку и зайти внутрь. Однако теперь
71+
// экземпляр одиночки уже будет создан и поток не сможет
72+
// пройти через это условие, а значит новый объект не будет
73+
// создан.
74+
if (_instance == null)
75+
{
76+
_instance = new Singleton();
77+
_instance.Value = value;
78+
}
5679
}
5780
}
5881
return _instance;
5982
}
83+
84+
// EN: We'll use this property to prove that our Singleton really works.
85+
//
86+
// RU: Мы используем это поле, чтобы доказать, что наш Одиночка
87+
// действительно работает.
88+
public string Value { get; set; }
6089
}
6190

6291
class Program
@@ -93,7 +122,7 @@ static void Main(string[] args)
93122
public static void TestSingleton(string value)
94123
{
95124
Singleton singleton = Singleton.GetInstance(value);
96-
Console.WriteLine(singleton.GetValue());
125+
Console.WriteLine(singleton.Value);
97126
}
98127
}
99128
}

0 commit comments

Comments
 (0)