├── .gitattributes ├── .gitignore ├── AbstractFactory.Conceptual ├── AbstractFactory.Conceptual.csproj ├── Output.txt └── Program.cs ├── Adapter.Conceptual ├── Adapter.Conceptual.csproj ├── Output.txt └── Program.cs ├── Bridge.Conceptual ├── Bridge.Conceptual.csproj ├── Output.txt └── Program.cs ├── Builder.Conceptual ├── Builder.Conceptual.csproj ├── Output.txt └── Program.cs ├── ChainOfResponsibility.Conceptual ├── ChainOfResponsibility.Conceptual.csproj ├── Output.txt └── Program.cs ├── Command.Conceptual ├── Command.Conceptual.csproj ├── Output.txt └── Program.cs ├── Composite.Conceptual ├── Composite.Conceptual.csproj ├── Output.txt └── Program.cs ├── Decorator.Conceptual ├── Decorator.Conceptual.csproj ├── Output.txt └── Program.cs ├── Directory.Build.props ├── Facade.Conceptual ├── Facade.Conceptual.csproj ├── Output.txt └── Program.cs ├── FactoryMethod.Conceptual ├── FactoryMethod.Conceptual.csproj ├── Output.txt └── Program.cs ├── Flyweight.Conceptual ├── Flyweight.Conceptual.csproj ├── Output.txt └── Program.cs ├── Iterator.Conceptual ├── Iterator.Conceptual.csproj ├── Output.txt └── Program.cs ├── LICENSE.txt ├── Mediator.Conceptual ├── Mediator.Conceptual.csproj ├── Output.txt └── Program.cs ├── Memento.Conceptual ├── Memento.Conceptual.csproj ├── Output.txt └── Program.cs ├── Observer.Conceptual ├── Observer.Conceptual.csproj ├── Output.txt └── Program.cs ├── Prototype.Conceptual ├── Output.txt ├── Program.cs └── Prototype.Conceptual.csproj ├── Proxy.Conceptual ├── Output.txt ├── Program.cs └── Proxy.Conceptual.csproj ├── README.md ├── RefactoringGuru.DesignPatterns.sln ├── Singleton.Conceptual ├── NonThreadSafe │ ├── Output.txt │ ├── Program.cs │ └── Singleton.Conceptual.NonThreadSafe.csproj └── ThreadSafe │ ├── Output.txt │ ├── Program.cs │ └── Singleton.Conceptual.ThreadSafe.csproj ├── State.Conceptual ├── Output.txt ├── Program.cs └── State.Conceptual.csproj ├── Strategy.Conceptual ├── Output.txt ├── Program.cs └── Strategy.Conceptual.csproj ├── TemplateMethod.Conceptual ├── Output.txt ├── Program.cs └── TemplateMethod.Conceptual.csproj └── Visitor.Conceptual ├── Output.txt ├── Program.cs └── Visitor.Conceptual.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=crlf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Autosave files 2 | *~ 3 | 4 | # build 5 | [Oo]bj/ 6 | [Bb]in/ 7 | packages/ 8 | TestResults/ 9 | 10 | # globs 11 | Makefile.in 12 | *.DS_Store 13 | *.sln.cache 14 | *.suo 15 | *.cache 16 | *.pidb 17 | *.userprefs 18 | *.usertasks 19 | config.log 20 | config.make 21 | config.status 22 | aclocal.m4 23 | install-sh 24 | autom4te.cache/ 25 | *.user 26 | *.tar.gz 27 | tarballs/ 28 | test-results/ 29 | Thumbs.db 30 | .vs/ 31 | 32 | # Mac bundle stuff 33 | *.dmg 34 | *.app 35 | 36 | # resharper 37 | *_Resharper.* 38 | *.Resharper 39 | 40 | # dotCover 41 | *.dotCover 42 | 43 | .idea/ 44 | -------------------------------------------------------------------------------- /AbstractFactory.Conceptual/AbstractFactory.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /AbstractFactory.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Client: Testing client code with the first factory type... 2 | The result of the product B1. 3 | The result of the B1 collaborating with the (The result of the product A1.) 4 | 5 | Client: Testing the same client code with the second factory type... 6 | The result of the product B2. 7 | The result of the B2 collaborating with the (The result of the product A2.) -------------------------------------------------------------------------------- /AbstractFactory.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Abstract Factory Design Pattern 2 | // 3 | // Intent: Lets you produce families of related objects without specifying their 4 | // concrete classes. 5 | // 6 | // RU: Паттерн Абстрактная Фабрика 7 | // 8 | // Назначение: Предоставляет интерфейс для создания семейств связанных или 9 | // зависимых объектов без привязки к их конкретным классам. 10 | 11 | using System; 12 | 13 | namespace RefactoringGuru.DesignPatterns.AbstractFactory.Conceptual 14 | { 15 | // EN: The Abstract Factory interface declares a set of methods that return 16 | // different abstract products. These products are called a family and are 17 | // related by a high-level theme or concept. Products of one family are 18 | // usually able to collaborate among themselves. A family of products may 19 | // have several variants, but the products of one variant are incompatible 20 | // with products of another. 21 | // 22 | // RU: Интерфейс Абстрактной Фабрики объявляет набор методов, которые 23 | // возвращают различные абстрактные продукты. Эти продукты называются 24 | // семейством и связаны темой или концепцией высокого уровня. Продукты 25 | // одного семейства обычно могут взаимодействовать между собой. Семейство 26 | // продуктов может иметь несколько вариаций, но продукты одной вариации 27 | // несовместимы с продуктами другой. 28 | public interface IAbstractFactory 29 | { 30 | IAbstractProductA CreateProductA(); 31 | 32 | IAbstractProductB CreateProductB(); 33 | } 34 | 35 | // EN: Concrete Factories produce a family of products that belong to a 36 | // single variant. The factory guarantees that resulting products are 37 | // compatible. Note that signatures of the Concrete Factory's methods return 38 | // an abstract product, while inside the method a concrete product is 39 | // instantiated. 40 | // 41 | // RU: Конкретная Фабрика производит семейство продуктов одной вариации. 42 | // Фабрика гарантирует совместимость полученных продуктов. Обратите 43 | // внимание, что сигнатуры методов Конкретной Фабрики возвращают абстрактный 44 | // продукт, в то время как внутри метода создается экземпляр конкретного 45 | // продукта. 46 | class ConcreteFactory1 : IAbstractFactory 47 | { 48 | public IAbstractProductA CreateProductA() 49 | { 50 | return new ConcreteProductA1(); 51 | } 52 | 53 | public IAbstractProductB CreateProductB() 54 | { 55 | return new ConcreteProductB1(); 56 | } 57 | } 58 | 59 | // EN: Each Concrete Factory has a corresponding product variant. 60 | // 61 | // RU: Каждая Конкретная Фабрика имеет соответствующую вариацию продукта. 62 | class ConcreteFactory2 : IAbstractFactory 63 | { 64 | public IAbstractProductA CreateProductA() 65 | { 66 | return new ConcreteProductA2(); 67 | } 68 | 69 | public IAbstractProductB CreateProductB() 70 | { 71 | return new ConcreteProductB2(); 72 | } 73 | } 74 | 75 | // EN: Each distinct product of a product family should have a base 76 | // interface. All variants of the product must implement this interface. 77 | // 78 | // RU: Каждый отдельный продукт семейства продуктов должен иметь базовый 79 | // интерфейс. Все вариации продукта должны реализовывать этот интерфейс. 80 | public interface IAbstractProductA 81 | { 82 | string UsefulFunctionA(); 83 | } 84 | 85 | // EN: Concrete Products are created by corresponding Concrete Factories. 86 | // 87 | // RU: Конкретные продукты создаются соответствующими Конкретными Фабриками. 88 | class ConcreteProductA1 : IAbstractProductA 89 | { 90 | public string UsefulFunctionA() 91 | { 92 | return "The result of the product A1."; 93 | } 94 | } 95 | 96 | class ConcreteProductA2 : IAbstractProductA 97 | { 98 | public string UsefulFunctionA() 99 | { 100 | return "The result of the product A2."; 101 | } 102 | } 103 | 104 | // EN: Here's the the base interface of another product. All products can 105 | // interact with each other, but proper interaction is possible only between 106 | // products of the same concrete variant. 107 | // 108 | // RU: Базовый интерфейс другого продукта. Все продукты могут 109 | // взаимодействовать друг с другом, но правильное взаимодействие возможно 110 | // только между продуктами одной и той же конкретной вариации. 111 | public interface IAbstractProductB 112 | { 113 | // EN: Product B is able to do its own thing... 114 | // 115 | // RU: Продукт B способен работать самостоятельно... 116 | string UsefulFunctionB(); 117 | 118 | // EN: ...but it also can collaborate with the ProductA. 119 | // 120 | // The Abstract Factory makes sure that all products it creates are of 121 | // the same variant and thus, compatible. 122 | // 123 | // RU: ...а также взаимодействовать с Продуктами А той же вариации. 124 | // 125 | // Абстрактная Фабрика гарантирует, что все продукты, которые она 126 | // создает, имеют одинаковую вариацию и, следовательно, совместимы. 127 | string AnotherUsefulFunctionB(IAbstractProductA collaborator); 128 | } 129 | 130 | // EN: Concrete Products are created by corresponding Concrete Factories. 131 | // 132 | // RU: Конкретные Продукты создаются соответствующими Конкретными Фабриками. 133 | class ConcreteProductB1 : IAbstractProductB 134 | { 135 | public string UsefulFunctionB() 136 | { 137 | return "The result of the product B1."; 138 | } 139 | 140 | // EN: The variant, Product B1, is only able to work correctly with the 141 | // variant, Product A1. Nevertheless, it accepts any instance of 142 | // AbstractProductA as an argument. 143 | // 144 | // RU: Продукт B1 может корректно работать только с Продуктом A1. Тем не 145 | // менее, он принимает любой экземпляр Абстрактного Продукта А в 146 | // качестве аргумента. 147 | public string AnotherUsefulFunctionB(IAbstractProductA collaborator) 148 | { 149 | var result = collaborator.UsefulFunctionA(); 150 | 151 | return $"The result of the B1 collaborating with the ({result})"; 152 | } 153 | } 154 | 155 | class ConcreteProductB2 : IAbstractProductB 156 | { 157 | public string UsefulFunctionB() 158 | { 159 | return "The result of the product B2."; 160 | } 161 | 162 | // EN: The variant, Product B2, is only able to work correctly with the 163 | // variant, Product A2. Nevertheless, it accepts any instance of 164 | // AbstractProductA as an argument. 165 | // 166 | // RU: Продукт B2 может корректно работать только с Продуктом A2. Тем не 167 | // менее, он принимает любой экземпляр Абстрактного Продукта А в качестве 168 | // аргумента. 169 | public string AnotherUsefulFunctionB(IAbstractProductA collaborator) 170 | { 171 | var result = collaborator.UsefulFunctionA(); 172 | 173 | return $"The result of the B2 collaborating with the ({result})"; 174 | } 175 | } 176 | 177 | // EN: The client code works with factories and products only through 178 | // abstract types: AbstractFactory and AbstractProduct. This lets you pass 179 | // any factory or product subclass to the client code without breaking it. 180 | // 181 | // RU: Клиентский код работает с фабриками и продуктами только через 182 | // абстрактные типы: Абстрактная Фабрика и Абстрактный Продукт. Это 183 | // позволяет передавать любой подкласс фабрики или продукта клиентскому 184 | // коду, не нарушая его. 185 | class Client 186 | { 187 | public void Main() 188 | { 189 | // EN: The client code can work with any concrete factory class. 190 | // 191 | // RU: Клиентский код может работать с любым конкретным классом 192 | // фабрики. 193 | Console.WriteLine("Client: Testing client code with the first factory type..."); 194 | ClientMethod(new ConcreteFactory1()); 195 | Console.WriteLine(); 196 | 197 | Console.WriteLine("Client: Testing the same client code with the second factory type..."); 198 | ClientMethod(new ConcreteFactory2()); 199 | } 200 | 201 | public void ClientMethod(IAbstractFactory factory) 202 | { 203 | var productA = factory.CreateProductA(); 204 | var productB = factory.CreateProductB(); 205 | 206 | Console.WriteLine(productB.UsefulFunctionB()); 207 | Console.WriteLine(productB.AnotherUsefulFunctionB(productA)); 208 | } 209 | } 210 | 211 | class Program 212 | { 213 | static void Main(string[] args) 214 | { 215 | new Client().Main(); 216 | } 217 | } 218 | } -------------------------------------------------------------------------------- /Adapter.Conceptual/Adapter.Conceptual.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Adapter.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Adaptee interface is incompatible with the client. 2 | But with adapter client can call it's method. 3 | This is 'Specific request.' -------------------------------------------------------------------------------- /Adapter.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Adapter Design Pattern 2 | // 3 | // Intent: Provides a unified interface that allows objects with incompatible 4 | // interfaces to collaborate. 5 | // 6 | // RU: Паттерн Адаптер 7 | // 8 | // Назначение: Позволяет объектам с несовместимыми интерфейсами работать вместе. 9 | 10 | using System; 11 | 12 | namespace RefactoringGuru.DesignPatterns.Adapter.Conceptual 13 | { 14 | // EN: The Target defines the domain-specific interface used by the client 15 | // code. 16 | // 17 | // RU: Целевой класс объявляет интерфейс, с которым может работать 18 | // клиентский код. 19 | public interface ITarget 20 | { 21 | string GetRequest(); 22 | } 23 | 24 | // EN: The Adaptee contains some useful behavior, but its interface is 25 | // incompatible with the existing client code. The Adaptee needs some 26 | // adaptation before the client code can use it. 27 | // 28 | // RU: Адаптируемый класс содержит некоторое полезное поведение, но его 29 | // интерфейс несовместим с существующим клиентским кодом. Адаптируемый 30 | // класс нуждается в некоторой доработке, прежде чем клиентский код сможет 31 | // его использовать. 32 | class Adaptee 33 | { 34 | public string GetSpecificRequest() 35 | { 36 | return "Specific request."; 37 | } 38 | } 39 | 40 | // EN: The Adapter makes the Adaptee's interface compatible with the 41 | // Target's interface. 42 | // 43 | // RU: Адаптер делает интерфейс Адаптируемого класса совместимым с целевым 44 | // интерфейсом. 45 | class Adapter : ITarget 46 | { 47 | private readonly Adaptee _adaptee; 48 | 49 | public Adapter(Adaptee adaptee) 50 | { 51 | this._adaptee = adaptee; 52 | } 53 | 54 | public string GetRequest() 55 | { 56 | return $"This is '{this._adaptee.GetSpecificRequest()}'"; 57 | } 58 | } 59 | 60 | class Program 61 | { 62 | static void Main(string[] args) 63 | { 64 | Adaptee adaptee = new Adaptee(); 65 | ITarget target = new Adapter(adaptee); 66 | 67 | Console.WriteLine("Adaptee interface is incompatible with the client."); 68 | Console.WriteLine("But with adapter client can call it's method."); 69 | 70 | Console.WriteLine(target.GetRequest()); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Bridge.Conceptual/Bridge.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Bridge.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Abstract: Base operation with: 2 | ConcreteImplementationA: The result in platform A. 3 | 4 | ExtendedAbstraction: Extended operation with: 5 | ConcreteImplementationA: The result in platform B. -------------------------------------------------------------------------------- /Bridge.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Bridge Design Pattern 2 | // 3 | // Intent: Lets you split a large class or a set of closely related classes into 4 | // two separate hierarchies—abstraction and implementation—which can be 5 | // developed independently of each other. 6 | // 7 | // A 8 | // / \ A N 9 | // Aa Ab ===> / \ / \ 10 | // / \ / \ Aa(N) Ab(N) 1 2 11 | // Aa1 Aa2 Ab1 Ab2 12 | // 13 | // RU: Паттерн Мост 14 | // 15 | // Назначение: Разделяет один или несколько классов на две отдельные иерархии — 16 | // абстракцию и реализацию, позволяя изменять их независимо друг от друга. 17 | // 18 | // A 19 | // / \ A N 20 | // Aa Ab ===> / \ / \ 21 | // / \ / \ Aa(N) Ab(N) 1 2 22 | // Aa1 Aa2 Ab1 Ab2 23 | 24 | using System; 25 | 26 | namespace RefactoringGuru.DesignPatterns.Bridge.Conceptual 27 | { 28 | // EN: The Abstraction defines the interface for the "control" part of the 29 | // two class hierarchies. It maintains a reference to an object of the 30 | // Implementation hierarchy and delegates all of the real work to this 31 | // object. 32 | // 33 | // RU: Абстракция устанавливает интерфейс для «управляющей» части двух 34 | // иерархий классов. Она содержит ссылку на объект из иерархии Реализации и 35 | // делегирует ему всю настоящую работу. 36 | class Abstraction 37 | { 38 | protected IImplementation _implementation; 39 | 40 | public Abstraction(IImplementation implementation) 41 | { 42 | this._implementation = implementation; 43 | } 44 | 45 | public virtual string Operation() 46 | { 47 | return "Abstract: Base operation with:\n" + 48 | _implementation.OperationImplementation(); 49 | } 50 | } 51 | 52 | // EN: You can extend the Abstraction without changing the Implementation 53 | // classes. 54 | // 55 | // RU: Можно расширить Абстракцию без изменения классов Реализации. 56 | class ExtendedAbstraction : Abstraction 57 | { 58 | public ExtendedAbstraction(IImplementation implementation) : base(implementation) 59 | { 60 | } 61 | 62 | public override string Operation() 63 | { 64 | return "ExtendedAbstraction: Extended operation with:\n" + 65 | base._implementation.OperationImplementation(); 66 | } 67 | } 68 | 69 | // EN: The Implementation defines the interface for all implementation 70 | // classes. It doesn't have to match the Abstraction's interface. In fact, 71 | // the two interfaces can be entirely different. Typically the 72 | // Implementation interface provides only primitive operations, while the 73 | // Abstraction defines higher- level operations based on those primitives. 74 | // 75 | // RU: Реализация устанавливает интерфейс для всех классов реализации. Он не 76 | // должен соответствовать интерфейсу Абстракции. На практике оба интерфейса 77 | // могут быть совершенно разными. Как правило, интерфейс Реализации 78 | // предоставляет только примитивные операции, в то время как Абстракция 79 | // определяет операции более высокого уровня, основанные на этих примитивах. 80 | public interface IImplementation 81 | { 82 | string OperationImplementation(); 83 | } 84 | 85 | // EN: Each Concrete Implementation corresponds to a specific platform and 86 | // implements the Implementation interface using that platform's API. 87 | // 88 | // RU: Каждая Конкретная Реализация соответствует определённой платформе и 89 | // реализует интерфейс Реализации с использованием API этой платформы. 90 | class ConcreteImplementationA : IImplementation 91 | { 92 | public string OperationImplementation() 93 | { 94 | return "ConcreteImplementationA: The result in platform A.\n"; 95 | } 96 | } 97 | 98 | class ConcreteImplementationB : IImplementation 99 | { 100 | public string OperationImplementation() 101 | { 102 | return "ConcreteImplementationB: The result in platform B.\n"; 103 | } 104 | } 105 | 106 | class Client 107 | { 108 | // EN: Except for the initialization phase, where an Abstraction object 109 | // gets linked with a specific Implementation object, the client code 110 | // should only depend on the Abstraction class. This way the client code 111 | // can support any abstraction-implementation combination. 112 | // 113 | // RU: За исключением этапа инициализации, когда объект Абстракции 114 | // связывается с определённым объектом Реализации, клиентский код должен 115 | // зависеть только от класса Абстракции. Таким образом, клиентский код 116 | // может поддерживать любую комбинацию абстракции и реализации. 117 | public void ClientCode(Abstraction abstraction) 118 | { 119 | Console.Write(abstraction.Operation()); 120 | } 121 | } 122 | 123 | class Program 124 | { 125 | static void Main(string[] args) 126 | { 127 | Client client = new Client(); 128 | 129 | Abstraction abstraction; 130 | // EN: The client code should be able to work with any pre- 131 | // configured abstraction-implementation combination. 132 | // 133 | // RU: Клиентский код должен работать с любой предварительно 134 | // сконфигурированной комбинацией абстракции и реализации. 135 | abstraction = new Abstraction(new ConcreteImplementationA()); 136 | client.ClientCode(abstraction); 137 | 138 | Console.WriteLine(); 139 | 140 | abstraction = new ExtendedAbstraction(new ConcreteImplementationB()); 141 | client.ClientCode(abstraction); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Builder.Conceptual/Builder.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Builder.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Standard basic product: 2 | Product parts: PartA1 3 | 4 | Standard full featured product: 5 | Product parts: PartA1, PartB1, PartC1 6 | 7 | Custom product: 8 | Product parts: PartA1, PartC1 -------------------------------------------------------------------------------- /Builder.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Builder Design Pattern 2 | // 3 | // Intent: Lets you construct complex objects step by step. The pattern allows 4 | // you to produce different types and representations of an object using the 5 | // same construction code. 6 | // 7 | // RU: Паттерн Строитель 8 | // 9 | // Назначение: Позволяет создавать сложные объекты пошагово. Строитель даёт 10 | // возможность использовать один и тот же код строительства для получения разных 11 | // представлений объектов. 12 | 13 | using System; 14 | using System.Collections.Generic; 15 | 16 | namespace RefactoringGuru.DesignPatterns.Builder.Conceptual 17 | { 18 | // EN: The Builder interface specifies methods for creating the different 19 | // parts of the Product objects. 20 | // 21 | // RU: Интерфейс Строителя объявляет создающие методы для различных частей 22 | // объектов Продуктов. 23 | public interface IBuilder 24 | { 25 | void BuildPartA(); 26 | 27 | void BuildPartB(); 28 | 29 | void BuildPartC(); 30 | } 31 | 32 | // EN: The Concrete Builder classes follow the Builder interface and provide 33 | // specific implementations of the building steps. Your program may have 34 | // several variations of Builders, implemented differently. 35 | // 36 | // RU: Классы Конкретного Строителя следуют интерфейсу Строителя и 37 | // предоставляют конкретные реализации шагов построения. Ваша программа 38 | // может иметь несколько вариантов Строителей, реализованных по-разному. 39 | public class ConcreteBuilder : IBuilder 40 | { 41 | private Product _product = new Product(); 42 | 43 | // EN: A fresh builder instance should contain a blank product object, 44 | // which is used in further assembly. 45 | // 46 | // RU: Новый экземпляр строителя должен содержать пустой объект 47 | // продукта, который используется в дальнейшей сборке. 48 | public ConcreteBuilder() 49 | { 50 | this.Reset(); 51 | } 52 | 53 | public void Reset() 54 | { 55 | this._product = new Product(); 56 | } 57 | 58 | // EN: All production steps work with the same product instance. 59 | // 60 | // RU: Все этапы производства работают с одним и тем же экземпляром 61 | // продукта. 62 | public void BuildPartA() 63 | { 64 | this._product.Add("PartA1"); 65 | } 66 | 67 | public void BuildPartB() 68 | { 69 | this._product.Add("PartB1"); 70 | } 71 | 72 | public void BuildPartC() 73 | { 74 | this._product.Add("PartC1"); 75 | } 76 | 77 | // EN: Concrete Builders are supposed to provide their own methods for 78 | // retrieving results. That's because various types of builders may 79 | // create entirely different products that don't follow the same 80 | // interface. Therefore, such methods cannot be declared in the base 81 | // Builder interface (at least in a statically typed programming 82 | // language). 83 | // 84 | // Usually, after returning the end result to the client, a builder 85 | // instance is expected to be ready to start producing another product. 86 | // That's why it's a usual practice to call the reset method at the end 87 | // of the `GetProduct` method body. However, this behavior is not 88 | // mandatory, and you can make your builders wait for an explicit reset 89 | // call from the client code before disposing of the previous result. 90 | // 91 | // RU: Конкретные Строители должны предоставить свои собственные методы 92 | // получения результатов. Это связано с тем, что различные типы 93 | // строителей могут создавать совершенно разные продукты с разными 94 | // интерфейсами. Поэтому такие методы не могут быть объявлены в базовом 95 | // интерфейсе Строителя (по крайней мере, в статически типизированном 96 | // языке программирования). 97 | // 98 | // Как правило, после возвращения конечного результата клиенту, 99 | // экземпляр строителя должен быть готов к началу производства 100 | // следующего продукта. Поэтому обычной практикой является вызов метода 101 | // сброса в конце тела метода GetProduct. Однако такое поведение не 102 | // является обязательным, вы можете заставить своих строителей ждать 103 | // явного запроса на сброс из кода клиента, прежде чем избавиться от 104 | // предыдущего результата. 105 | public Product GetProduct() 106 | { 107 | Product result = this._product; 108 | 109 | this.Reset(); 110 | 111 | return result; 112 | } 113 | } 114 | 115 | // EN: It makes sense to use the Builder pattern only when your products are 116 | // quite complex and require extensive configuration. 117 | // 118 | // Unlike in other creational patterns, different concrete builders can 119 | // produce unrelated products. In other words, results of various builders 120 | // may not always follow the same interface. 121 | // 122 | // RU: Имеет смысл использовать паттерн Строитель только тогда, когда ваши 123 | // продукты достаточно сложны и требуют обширной конфигурации. 124 | // 125 | // В отличие от других порождающих паттернов, различные конкретные строители 126 | // могут производить несвязанные продукты. Другими словами, результаты 127 | // различных строителей могут не всегда следовать одному и тому же 128 | // интерфейсу. 129 | public class Product 130 | { 131 | private List _parts = new List(); 132 | 133 | public void Add(string part) 134 | { 135 | this._parts.Add(part); 136 | } 137 | 138 | public string ListParts() 139 | { 140 | string str = string.Empty; 141 | 142 | for (int i = 0; i < this._parts.Count; i++) 143 | { 144 | str += this._parts[i] + ", "; 145 | } 146 | 147 | str = str.Remove(str.Length - 2); // removing last ",c" 148 | 149 | return "Product parts: " + str + "\n"; 150 | } 151 | } 152 | 153 | // EN: The Director is only responsible for executing the building steps in 154 | // a particular sequence. It is helpful when producing products according to 155 | // a specific order or configuration. Strictly speaking, the Director class 156 | // is optional, since the client can control builders directly. 157 | // 158 | // RU: Директор отвечает только за выполнение шагов построения в 159 | // определённой последовательности. Это полезно при производстве продуктов в 160 | // определённом порядке или особой конфигурации. Строго говоря, класс 161 | // Директор необязателен, так как клиент может напрямую управлять 162 | // строителями. 163 | public class Director 164 | { 165 | private IBuilder _builder; 166 | 167 | public IBuilder Builder 168 | { 169 | set { _builder = value; } 170 | } 171 | 172 | // EN: The Director can construct several product variations using the 173 | // same building steps. 174 | // 175 | // RU: Директор может строить несколько вариаций продукта, используя 176 | // одинаковые шаги построения. 177 | public void BuildMinimalViableProduct() 178 | { 179 | this._builder.BuildPartA(); 180 | } 181 | 182 | public void BuildFullFeaturedProduct() 183 | { 184 | this._builder.BuildPartA(); 185 | this._builder.BuildPartB(); 186 | this._builder.BuildPartC(); 187 | } 188 | } 189 | 190 | class Program 191 | { 192 | static void Main(string[] args) 193 | { 194 | // EN: The client code creates a builder object, passes it to the 195 | // director and then initiates the construction process. The end 196 | // result is retrieved from the builder object. 197 | // 198 | // RU: Клиентский код создаёт объект-строитель, передаёт его 199 | // директору, а затем инициирует процесс построения. Конечный 200 | // результат извлекается из объекта-строителя. 201 | var director = new Director(); 202 | var builder = new ConcreteBuilder(); 203 | director.Builder = builder; 204 | 205 | Console.WriteLine("Standard basic product:"); 206 | director.BuildMinimalViableProduct(); 207 | Console.WriteLine(builder.GetProduct().ListParts()); 208 | 209 | Console.WriteLine("Standard full featured product:"); 210 | director.BuildFullFeaturedProduct(); 211 | Console.WriteLine(builder.GetProduct().ListParts()); 212 | 213 | // EN: Remember, the Builder pattern can be used without a Director 214 | // class. 215 | // 216 | // RU: Помните, что паттерн Строитель можно использовать без класса 217 | // Директор. 218 | Console.WriteLine("Custom product:"); 219 | builder.BuildPartA(); 220 | builder.BuildPartC(); 221 | Console.Write(builder.GetProduct().ListParts()); 222 | } 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /ChainOfResponsibility.Conceptual/ChainOfResponsibility.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /ChainOfResponsibility.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Chain: Monkey > Squirrel > Dog 2 | 3 | Client: Who wants a Nut? 4 | Squirrel: I'll eat the Nut. 5 | Client: Who wants a Banana? 6 | Monkey: I'll eat the Banana. 7 | Client: Who wants a Cup of coffee? 8 | Cup of coffee was left untouched. 9 | 10 | Subchain: Squirrel > Dog 11 | 12 | Client: Who wants a Nut? 13 | Squirrel: I'll eat the Nut. 14 | Client: Who wants a Banana? 15 | Banana was left untouched. 16 | Client: Who wants a Cup of coffee? 17 | Cup of coffee was left untouched. -------------------------------------------------------------------------------- /ChainOfResponsibility.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Chain of Responsibility Design Pattern 2 | // 3 | // Intent: Lets you pass requests along a chain of handlers. Upon receiving a 4 | // request, each handler decides either to process the request or to pass it to 5 | // the next handler in the chain. 6 | // 7 | // RU: Паттерн Цепочка обязанностей 8 | // 9 | // Назначение: Позволяет передавать запросы последовательно по цепочке 10 | // обработчиков. Каждый последующий обработчик решает, может ли он обработать 11 | // запрос сам и стоит ли передавать запрос дальше по цепи. 12 | 13 | using System; 14 | using System.Collections.Generic; 15 | 16 | namespace RefactoringGuru.DesignPatterns.ChainOfResponsibility.Conceptual 17 | { 18 | // EN: The Handler interface declares a method for building the chain of 19 | // handlers. It also declares a method for executing a request. 20 | // 21 | // RU: Интерфейс Обработчика объявляет метод построения цепочки 22 | // обработчиков. Он также объявляет метод для выполнения запроса. 23 | public interface IHandler 24 | { 25 | IHandler SetNext(IHandler handler); 26 | 27 | object Handle(object request); 28 | } 29 | 30 | // EN: The default chaining behavior can be implemented inside a base 31 | // handler class. 32 | // 33 | // RU: Поведение цепочки по умолчанию может быть реализовано внутри базового 34 | // класса обработчика. 35 | abstract class AbstractHandler : IHandler 36 | { 37 | private IHandler _nextHandler; 38 | 39 | public IHandler SetNext(IHandler handler) 40 | { 41 | this._nextHandler = handler; 42 | 43 | // EN: Returning a handler from here will let us link handlers in a 44 | // convenient way like this: 45 | // monkey.SetNext(squirrel).SetNext(dog); 46 | // 47 | // RU: Возврат обработчика отсюда позволит связать обработчики 48 | // простым способом, вот так: 49 | // monkey.SetNext(squirrel).SetNext(dog); 50 | return handler; 51 | } 52 | 53 | public virtual object Handle(object request) 54 | { 55 | if (this._nextHandler != null) 56 | { 57 | return this._nextHandler.Handle(request); 58 | } 59 | else 60 | { 61 | return null; 62 | } 63 | } 64 | } 65 | 66 | class MonkeyHandler : AbstractHandler 67 | { 68 | public override object Handle(object request) 69 | { 70 | if ((request as string) == "Banana") 71 | { 72 | return $"Monkey: I'll eat the {request.ToString()}.\n"; 73 | } 74 | else 75 | { 76 | return base.Handle(request); 77 | } 78 | } 79 | } 80 | 81 | class SquirrelHandler : AbstractHandler 82 | { 83 | public override object Handle(object request) 84 | { 85 | if (request.ToString() == "Nut") 86 | { 87 | return $"Squirrel: I'll eat the {request.ToString()}.\n"; 88 | } 89 | else 90 | { 91 | return base.Handle(request); 92 | } 93 | } 94 | } 95 | 96 | class DogHandler : AbstractHandler 97 | { 98 | public override object Handle(object request) 99 | { 100 | if (request.ToString() == "MeatBall") 101 | { 102 | return $"Dog: I'll eat the {request.ToString()}.\n"; 103 | } 104 | else 105 | { 106 | return base.Handle(request); 107 | } 108 | } 109 | } 110 | 111 | class Client 112 | { 113 | // EN: The client code is usually suited to work with a single handler. 114 | // In most cases, it is not even aware that the handler is part of a 115 | // chain. 116 | // 117 | // RU: Обычно клиентский код приспособлен для работы с единственным 118 | // обработчиком. В большинстве случаев клиенту даже неизвестно, что этот 119 | // обработчик является частью цепочки. 120 | public static void ClientCode(AbstractHandler handler) 121 | { 122 | foreach (var food in new List { "Nut", "Banana", "Cup of coffee" }) 123 | { 124 | Console.WriteLine($"Client: Who wants a {food}?"); 125 | 126 | var result = handler.Handle(food); 127 | 128 | if (result != null) 129 | { 130 | Console.Write($" {result}"); 131 | } 132 | else 133 | { 134 | Console.WriteLine($" {food} was left untouched."); 135 | } 136 | } 137 | } 138 | } 139 | 140 | class Program 141 | { 142 | static void Main(string[] args) 143 | { 144 | // EN: The other part of the client code constructs the actual 145 | // chain. 146 | // 147 | // RU: Другая часть клиентского кода создает саму цепочку. 148 | var monkey = new MonkeyHandler(); 149 | var squirrel = new SquirrelHandler(); 150 | var dog = new DogHandler(); 151 | 152 | monkey.SetNext(squirrel).SetNext(dog); 153 | 154 | // EN: The client should be able to send a request to any handler, 155 | // not just the first one in the chain. 156 | // 157 | // RU: Клиент должен иметь возможность отправлять запрос любому 158 | // обработчику, а не только первому в цепочке. 159 | Console.WriteLine("Chain: Monkey > Squirrel > Dog\n"); 160 | Client.ClientCode(monkey); 161 | Console.WriteLine(); 162 | 163 | Console.WriteLine("Subchain: Squirrel > Dog\n"); 164 | Client.ClientCode(squirrel); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Command.Conceptual/Command.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Command.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Invoker: Does anybody want something done before I begin? 2 | SimpleCommand: See, I can do simple things like printing (Say Hi!) 3 | Invoker: ...doing something really important... 4 | Invoker: Does anybody want something done after I finish? 5 | ComplexCommand: Complex stuff should be done by a receiver object. 6 | Receiver: Working on (Send email.) 7 | Receiver: Also working on (Save report.) -------------------------------------------------------------------------------- /Command.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Command Design Pattern 2 | // 3 | // Intent: Turns a request into a stand-alone object that contains all 4 | // information about the request. This transformation lets you parameterize 5 | // methods with different requests, delay or queue a request's execution, and 6 | // support undoable operations. 7 | // 8 | // RU: Паттерн Команда 9 | // 10 | // Назначение: Превращает запросы в объекты, позволяя передавать их как 11 | // аргументы при вызове методов, ставить запросы в очередь, логировать их, а 12 | // также поддерживать отмену операций. 13 | 14 | using System; 15 | 16 | namespace RefactoringGuru.DesignPatterns.Command.Conceptual 17 | { 18 | // EN: The Command interface declares a method for executing a command. 19 | // 20 | // RU: Интерфейс Команды объявляет метод для выполнения команд. 21 | public interface ICommand 22 | { 23 | void Execute(); 24 | } 25 | 26 | // EN: Some commands can implement simple operations on their own. 27 | // 28 | // RU: Некоторые команды способны выполнять простые операции самостоятельно. 29 | class SimpleCommand : ICommand 30 | { 31 | private string _payload = string.Empty; 32 | 33 | public SimpleCommand(string payload) 34 | { 35 | this._payload = payload; 36 | } 37 | 38 | public void Execute() 39 | { 40 | Console.WriteLine($"SimpleCommand: See, I can do simple things like printing ({this._payload})"); 41 | } 42 | } 43 | 44 | // EN: However, some commands can delegate more complex operations to other 45 | // objects, called "receivers." 46 | // 47 | // RU: Но есть и команды, которые делегируют более сложные операции другим 48 | // объектам, называемым «получателями». 49 | class ComplexCommand : ICommand 50 | { 51 | private Receiver _receiver; 52 | 53 | // EN: Context data, required for launching the receiver's methods. 54 | // 55 | // RU: Данные о контексте, необходимые для запуска методов получателя. 56 | private string _a; 57 | 58 | private string _b; 59 | 60 | // EN: Complex commands can accept one or several receiver objects along 61 | // with any context data via the constructor. 62 | // 63 | // RU: Сложные команды могут принимать один или несколько 64 | // объектов-получателей вместе с любыми данными о контексте через 65 | // конструктор. 66 | public ComplexCommand(Receiver receiver, string a, string b) 67 | { 68 | this._receiver = receiver; 69 | this._a = a; 70 | this._b = b; 71 | } 72 | 73 | // EN: Commands can delegate to any methods of a receiver. 74 | // 75 | // RU: Команды могут делегировать выполнение любым методам получателя. 76 | public void Execute() 77 | { 78 | Console.WriteLine("ComplexCommand: Complex stuff should be done by a receiver object."); 79 | this._receiver.DoSomething(this._a); 80 | this._receiver.DoSomethingElse(this._b); 81 | } 82 | } 83 | 84 | // EN: The Receiver classes contain some important business logic. They know 85 | // how to perform all kinds of operations, associated with carrying out a 86 | // request. In fact, any class may serve as a Receiver. 87 | // 88 | // RU: Классы Получателей содержат некую важную бизнес-логику. Они умеют 89 | // выполнять все виды операций, связанных с выполнением запроса. Фактически, 90 | // любой класс может выступать Получателем. 91 | class Receiver 92 | { 93 | public void DoSomething(string a) 94 | { 95 | Console.WriteLine($"Receiver: Working on ({a}.)"); 96 | } 97 | 98 | public void DoSomethingElse(string b) 99 | { 100 | Console.WriteLine($"Receiver: Also working on ({b}.)"); 101 | } 102 | } 103 | 104 | // EN: The Invoker is associated with one or several commands. It sends a 105 | // request to the command. 106 | // 107 | // RU: Отправитель связан с одной или несколькими командами. Он отправляет 108 | // запрос команде. 109 | class Invoker 110 | { 111 | private ICommand _onStart; 112 | 113 | private ICommand _onFinish; 114 | 115 | // EN: Initialize commands. 116 | // 117 | // RU: Инициализация команд 118 | public void SetOnStart(ICommand command) 119 | { 120 | this._onStart = command; 121 | } 122 | 123 | public void SetOnFinish(ICommand command) 124 | { 125 | this._onFinish = command; 126 | } 127 | 128 | // EN: The Invoker does not depend on concrete command or receiver 129 | // classes. The Invoker passes a request to a receiver indirectly, by 130 | // executing a command. 131 | // 132 | // RU: Отправитель не зависит от классов конкретных команд и 133 | // получателей. Отправитель передаёт запрос получателю косвенно, 134 | // выполняя команду. 135 | public void DoSomethingImportant() 136 | { 137 | Console.WriteLine("Invoker: Does anybody want something done before I begin?"); 138 | if (this._onStart is ICommand) 139 | { 140 | this._onStart.Execute(); 141 | } 142 | 143 | Console.WriteLine("Invoker: ...doing something really important..."); 144 | 145 | Console.WriteLine("Invoker: Does anybody want something done after I finish?"); 146 | if (this._onFinish is ICommand) 147 | { 148 | this._onFinish.Execute(); 149 | } 150 | } 151 | } 152 | 153 | class Program 154 | { 155 | static void Main(string[] args) 156 | { 157 | // EN: The client code can parameterize an invoker with any 158 | // commands. 159 | // 160 | // RU: Клиентский код может параметризовать отправителя любыми 161 | // командами. 162 | Invoker invoker = new Invoker(); 163 | invoker.SetOnStart(new SimpleCommand("Say Hi!")); 164 | Receiver receiver = new Receiver(); 165 | invoker.SetOnFinish(new ComplexCommand(receiver, "Send email", "Save report")); 166 | 167 | invoker.DoSomethingImportant(); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Composite.Conceptual/Composite.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Composite.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Client: I get a simple component: 2 | RESULT: Leaf 3 | 4 | Client: Now I've got a composite tree: 5 | RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf)) 6 | 7 | Client: I don't need to check the components classes even when managing the tree: 8 | RESULT: Branch(Branch(Leaf+Leaf)+Branch(Leaf)+Leaf) -------------------------------------------------------------------------------- /Composite.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Composite Design Pattern 2 | // 3 | // Intent: Lets you compose objects into tree structures and then work with 4 | // these structures as if they were individual objects. 5 | // 6 | // RU: Паттерн Компоновщик 7 | // 8 | // Назначение: Позволяет сгруппировать объекты в древовидную структуру, а затем 9 | // работать с ними так, как будто это единичный объект. 10 | 11 | using System; 12 | using System.Collections.Generic; 13 | 14 | namespace RefactoringGuru.DesignPatterns.Composite.Conceptual 15 | { 16 | // EN: The base Component class declares common operations for both simple 17 | // and complex objects of a composition. 18 | // 19 | // RU: Базовый класс Компонент объявляет общие операции как для простых, так 20 | // и для сложных объектов структуры. 21 | abstract class Component 22 | { 23 | public Component() { } 24 | 25 | // EN: The base Component may implement some default behavior or leave 26 | // it to concrete classes (by declaring the method containing the 27 | // behavior as "abstract"). 28 | // 29 | // RU: Базовый Компонент может сам реализовать некоторое поведение по 30 | // умолчанию или поручить это конкретным классам, объявив метод, 31 | // содержащий поведение абстрактным. 32 | public abstract string Operation(); 33 | 34 | // EN: In some cases, it would be beneficial to define the child- 35 | // management operations right in the base Component class. This way, 36 | // you won't need to expose any concrete component classes to the client 37 | // code, even during the object tree assembly. The downside is that 38 | // these methods will be empty for the leaf-level components. 39 | // 40 | // RU: В некоторых случаях целесообразно определить операции управления 41 | // потомками прямо в базовом классе Компонент. Таким образом, вам не 42 | // нужно будет предоставлять конкретные классы компонентов клиентскому 43 | // коду, даже во время сборки дерева объектов. Недостаток такого подхода 44 | // в том, что эти методы будут пустыми для компонентов уровня листа. 45 | public virtual void Add(Component component) 46 | { 47 | throw new NotImplementedException(); 48 | } 49 | 50 | public virtual void Remove(Component component) 51 | { 52 | throw new NotImplementedException(); 53 | } 54 | 55 | // EN: You can provide a method that lets the client code figure out 56 | // whether a component can bear children. 57 | // 58 | // RU: Вы можете предоставить метод, который позволит клиентскому коду 59 | // понять, может ли компонент иметь вложенные объекты. 60 | public virtual bool IsComposite() 61 | { 62 | return true; 63 | } 64 | } 65 | 66 | // EN: The Leaf class represents the end objects of a composition. A leaf 67 | // can't have any children. 68 | // 69 | // Usually, it's the Leaf objects that do the actual work, whereas Composite 70 | // objects only delegate to their sub-components. 71 | // 72 | // RU: Класс Лист представляет собой конечные объекты структуры. Лист не 73 | // может иметь вложенных компонентов. 74 | // 75 | // Обычно объекты Листьев выполняют фактическую работу, тогда как объекты 76 | // Контейнера лишь делегируют работу своим подкомпонентам. 77 | class Leaf : Component 78 | { 79 | public override string Operation() 80 | { 81 | return "Leaf"; 82 | } 83 | 84 | public override bool IsComposite() 85 | { 86 | return false; 87 | } 88 | } 89 | 90 | // EN: The Composite class represents the complex components that may have 91 | // children. Usually, the Composite objects delegate the actual work to 92 | // their children and then "sum-up" the result. 93 | // 94 | // RU: Класс Контейнер содержит сложные компоненты, которые могут иметь 95 | // вложенные компоненты. Обычно объекты Контейнеры делегируют фактическую 96 | // работу своим детям, а затем «суммируют» результат. 97 | class Composite : Component 98 | { 99 | protected List _children = new List(); 100 | 101 | public override void Add(Component component) 102 | { 103 | this._children.Add(component); 104 | } 105 | 106 | public override void Remove(Component component) 107 | { 108 | this._children.Remove(component); 109 | } 110 | 111 | // EN: The Composite executes its primary logic in a particular way. It 112 | // traverses recursively through all its children, collecting and 113 | // summing their results. Since the composite's children pass these 114 | // calls to their children and so forth, the whole object tree is 115 | // traversed as a result. 116 | // 117 | // RU: Контейнер выполняет свою основную логику особым образом. Он 118 | // проходит рекурсивно через всех своих детей, собирая и суммируя их 119 | // результаты. Поскольку потомки контейнера передают эти вызовы своим 120 | // потомкам и так далее, в результате обходится всё дерево объектов. 121 | public override string Operation() 122 | { 123 | int i = 0; 124 | string result = "Branch("; 125 | 126 | foreach (Component component in this._children) 127 | { 128 | result += component.Operation(); 129 | if (i != this._children.Count - 1) 130 | { 131 | result += "+"; 132 | } 133 | i++; 134 | } 135 | 136 | return result + ")"; 137 | } 138 | } 139 | 140 | class Client 141 | { 142 | // EN: The client code works with all of the components via the base 143 | // interface. 144 | // 145 | // RU: Клиентский код работает со всеми компонентами через базовый 146 | // интерфейс. 147 | public void ClientCode(Component leaf) 148 | { 149 | Console.WriteLine($"RESULT: {leaf.Operation()}\n"); 150 | } 151 | 152 | // EN: Thanks to the fact that the child-management operations are 153 | // declared in the base Component class, the client code can work with 154 | // any component, simple or complex, without depending on their concrete 155 | // classes. 156 | // 157 | // RU: Благодаря тому, что операции управления потомками объявлены в 158 | // базовом классе Компонента, клиентский код может работать как с 159 | // простыми, так и со сложными компонентами, вне зависимости от их 160 | // конкретных классов. 161 | public void ClientCode2(Component component1, Component component2) 162 | { 163 | if (component1.IsComposite()) 164 | { 165 | component1.Add(component2); 166 | } 167 | 168 | Console.WriteLine($"RESULT: {component1.Operation()}"); 169 | } 170 | } 171 | 172 | class Program 173 | { 174 | static void Main(string[] args) 175 | { 176 | Client client = new Client(); 177 | 178 | // EN: This way the client code can support the simple leaf 179 | // components... 180 | // 181 | // RU: Таким образом, клиентский код может поддерживать простые 182 | // компоненты-листья... 183 | Leaf leaf = new Leaf(); 184 | Console.WriteLine("Client: I get a simple component:"); 185 | client.ClientCode(leaf); 186 | 187 | // EN: ...as well as the complex composites. 188 | // 189 | // RU: ...а также сложные контейнеры. 190 | Composite tree = new Composite(); 191 | Composite branch1 = new Composite(); 192 | branch1.Add(new Leaf()); 193 | branch1.Add(new Leaf()); 194 | Composite branch2 = new Composite(); 195 | branch2.Add(new Leaf()); 196 | tree.Add(branch1); 197 | tree.Add(branch2); 198 | Console.WriteLine("Client: Now I've got a composite tree:"); 199 | client.ClientCode(tree); 200 | 201 | Console.Write("Client: I don't need to check the components classes even when managing the tree:\n"); 202 | client.ClientCode2(tree, leaf); 203 | } 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /Decorator.Conceptual/Decorator.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Decorator.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Client: I get a simple component: 2 | RESULT: ConcreteComponent 3 | 4 | Client: Now I've got a decorated component: 5 | RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent)) -------------------------------------------------------------------------------- /Decorator.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Decorator Design Pattern 2 | // 3 | // Intent: Lets you attach new behaviors to objects by placing these objects 4 | // inside special wrapper objects that contain the behaviors. 5 | // 6 | // RU: Паттерн Декоратор 7 | // 8 | // Назначение: Позволяет динамически добавлять объектам новую функциональность, 9 | // оборачивая их в полезные «обёртки». 10 | 11 | using System; 12 | 13 | namespace RefactoringGuru.DesignPatterns.Composite.Conceptual 14 | { 15 | // EN: The base Component interface defines operations that can be altered 16 | // by decorators. 17 | // 18 | // RU: Базовый интерфейс Компонента определяет поведение, которое изменяется 19 | // декораторами. 20 | public abstract class Component 21 | { 22 | public abstract string Operation(); 23 | } 24 | 25 | // EN: Concrete Components provide default implementations of the 26 | // operations. There might be several variations of these classes. 27 | // 28 | // RU: Конкретные Компоненты предоставляют реализации поведения по 29 | // умолчанию. Может быть несколько вариаций этих классов. 30 | class ConcreteComponent : Component 31 | { 32 | public override string Operation() 33 | { 34 | return "ConcreteComponent"; 35 | } 36 | } 37 | 38 | // EN: The base Decorator class follows the same interface as the other 39 | // components. The primary purpose of this class is to define the wrapping 40 | // interface for all concrete decorators. The default implementation of the 41 | // wrapping code might include a field for storing a wrapped component and 42 | // the means to initialize it. 43 | // 44 | // RU: Базовый класс Декоратора следует тому же интерфейсу, что и другие 45 | // компоненты. Основная цель этого класса - определить интерфейс обёртки для 46 | // всех конкретных декораторов. Реализация кода обёртки по умолчанию может 47 | // включать в себя поле для хранения завёрнутого компонента и средства его 48 | // инициализации. 49 | abstract class Decorator : Component 50 | { 51 | protected Component _component; 52 | 53 | public Decorator(Component component) 54 | { 55 | this._component = component; 56 | } 57 | 58 | public void SetComponent(Component component) 59 | { 60 | this._component = component; 61 | } 62 | 63 | // EN: The Decorator delegates all work to the wrapped component. 64 | // 65 | // RU: Декоратор делегирует всю работу обёрнутому компоненту. 66 | public override string Operation() 67 | { 68 | if (this._component != null) 69 | { 70 | return this._component.Operation(); 71 | } 72 | else 73 | { 74 | return string.Empty; 75 | } 76 | } 77 | } 78 | 79 | // EN: Concrete Decorators call the wrapped object and alter its result in 80 | // some way. 81 | // 82 | // RU: Конкретные Декораторы вызывают обёрнутый объект и изменяют его 83 | // результат некоторым образом. 84 | class ConcreteDecoratorA : Decorator 85 | { 86 | public ConcreteDecoratorA(Component comp) : base(comp) 87 | { 88 | } 89 | 90 | // EN: Decorators may call parent implementation of the operation, 91 | // instead of calling the wrapped object directly. This approach 92 | // simplifies extension of decorator classes. 93 | // 94 | // RU: Декораторы могут вызывать родительскую реализацию операции, 95 | // вместо того, чтобы вызвать обёрнутый объект напрямую. Такой подход 96 | // упрощает расширение классов декораторов. 97 | public override string Operation() 98 | { 99 | return $"ConcreteDecoratorA({base.Operation()})"; 100 | } 101 | } 102 | 103 | // EN: Decorators can execute their behavior either before or after the call 104 | // to a wrapped object. 105 | // 106 | // RU: Декораторы могут выполнять своё поведение до или после вызова 107 | // обёрнутого объекта. 108 | class ConcreteDecoratorB : Decorator 109 | { 110 | public ConcreteDecoratorB(Component comp) : base(comp) 111 | { 112 | } 113 | 114 | public override string Operation() 115 | { 116 | return $"ConcreteDecoratorB({base.Operation()})"; 117 | } 118 | } 119 | 120 | public class Client 121 | { 122 | // EN: The client code works with all objects using the Component 123 | // interface. This way it can stay independent of the concrete classes 124 | // of components it works with. 125 | // 126 | // RU: Клиентский код работает со всеми объектами, используя интерфейс 127 | // Компонента. Таким образом, он остаётся независимым от конкретных 128 | // классов компонентов, с которыми работает. 129 | public void ClientCode(Component component) 130 | { 131 | Console.WriteLine("RESULT: " + component.Operation()); 132 | } 133 | } 134 | 135 | class Program 136 | { 137 | static void Main(string[] args) 138 | { 139 | Client client = new Client(); 140 | 141 | var simple = new ConcreteComponent(); 142 | Console.WriteLine("Client: I get a simple component:"); 143 | client.ClientCode(simple); 144 | Console.WriteLine(); 145 | 146 | // EN: ...as well as decorated ones. 147 | // 148 | // Note how decorators can wrap not only simple components but the 149 | // other decorators as well. 150 | // 151 | // RU: ...так и декорированные. 152 | // 153 | // Обратите внимание, что декораторы могут обёртывать не только 154 | // простые компоненты, но и другие декораторы. 155 | ConcreteDecoratorA decorator1 = new ConcreteDecoratorA(simple); 156 | ConcreteDecoratorB decorator2 = new ConcreteDecoratorB(decorator1); 157 | Console.WriteLine("Client: Now I've got a decorated component:"); 158 | client.ClientCode(decorator2); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | disable 6 | disable 7 | https://github.com/RefactoringGuru/design-patterns-csharp 8 | Git 9 | True 10 | True 11 | 12 | -------------------------------------------------------------------------------- /Facade.Conceptual/Facade.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Facade.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Facade initializes subsystems: 2 | Subsystem1: Ready! 3 | Subsystem2: Get ready! 4 | Facade orders subsystems to perform the action: 5 | Subsystem1: Go! 6 | Subsystem2: Fire! -------------------------------------------------------------------------------- /Facade.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Facade Design Pattern 2 | // 3 | // Intent: Provides a simplified interface to a library, a framework, or any 4 | // other complex set of classes. 5 | // 6 | // RU: Паттерн Фасад 7 | // 8 | // Назначение: Предоставляет простой интерфейс к сложной системе классов, 9 | // библиотеке или фреймворку. 10 | 11 | using System; 12 | 13 | namespace RefactoringGuru.DesignPatterns.Facade.Conceptual 14 | { 15 | // EN: The Facade class provides a simple interface to the complex logic of 16 | // one or several subsystems. The Facade delegates the client requests to 17 | // the appropriate objects within the subsystem. The Facade is also 18 | // responsible for managing their lifecycle. All of this shields the client 19 | // from the undesired complexity of the subsystem. 20 | // 21 | // RU: Класс Фасада предоставляет простой интерфейс для сложной логики одной 22 | // или нескольких подсистем. Фасад делегирует запросы клиентов 23 | // соответствующим объектам внутри подсистемы. Фасад также отвечает за 24 | // управление их жизненным циклом. Все это защищает клиента от нежелательной 25 | // сложности подсистемы. 26 | public class Facade 27 | { 28 | protected Subsystem1 _subsystem1; 29 | 30 | protected Subsystem2 _subsystem2; 31 | 32 | public Facade(Subsystem1 subsystem1, Subsystem2 subsystem2) 33 | { 34 | this._subsystem1 = subsystem1; 35 | this._subsystem2 = subsystem2; 36 | } 37 | 38 | // EN: The Facade's methods are convenient shortcuts to the 39 | // sophisticated functionality of the subsystems. However, clients get 40 | // only to a fraction of a subsystem's capabilities. 41 | // 42 | // RU: Методы Фасада удобны для быстрого доступа к сложной 43 | // функциональности подсистем. Однако клиенты получают только часть 44 | // возможностей подсистемы. 45 | public string Operation() 46 | { 47 | string result = "Facade initializes subsystems:\n"; 48 | result += this._subsystem1.operation1(); 49 | result += this._subsystem2.operation1(); 50 | result += "Facade orders subsystems to perform the action:\n"; 51 | result += this._subsystem1.operationN(); 52 | result += this._subsystem2.operationZ(); 53 | return result; 54 | } 55 | } 56 | 57 | // EN: The Subsystem can accept requests either from the facade or client 58 | // directly. In any case, to the Subsystem, the Facade is yet another 59 | // client, and it's not a part of the Subsystem. 60 | // 61 | // RU: Подсистема может принимать запросы либо от фасада, либо от клиента 62 | // напрямую. В любом случае, для Подсистемы Фасад – это еще один клиент, и 63 | // он не является частью Подсистемы. 64 | public class Subsystem1 65 | { 66 | public string operation1() 67 | { 68 | return "Subsystem1: Ready!\n"; 69 | } 70 | 71 | public string operationN() 72 | { 73 | return "Subsystem1: Go!\n"; 74 | } 75 | } 76 | 77 | // EN: Some facades can work with multiple subsystems at the same time. 78 | // 79 | // RU: Некоторые фасады могут работать с разными подсистемами одновременно. 80 | public class Subsystem2 81 | { 82 | public string operation1() 83 | { 84 | return "Subsystem2: Get ready!\n"; 85 | } 86 | 87 | public string operationZ() 88 | { 89 | return "Subsystem2: Fire!\n"; 90 | } 91 | } 92 | 93 | 94 | class Client 95 | { 96 | // EN: The client code works with complex subsystems through a simple 97 | // interface provided by the Facade. When a facade manages the lifecycle 98 | // of the subsystem, the client might not even know about the existence 99 | // of the subsystem. This approach lets you keep the complexity under 100 | // control. 101 | // 102 | // RU: Клиентский код работает со сложными подсистемами через простой 103 | // интерфейс, предоставляемый Фасадом. Когда фасад управляет жизненным 104 | // циклом подсистемы, клиент может даже не знать о существовании 105 | // подсистемы. Такой подход позволяет держать сложность под контролем. 106 | public static void ClientCode(Facade facade) 107 | { 108 | Console.Write(facade.Operation()); 109 | } 110 | } 111 | 112 | class Program 113 | { 114 | static void Main(string[] args) 115 | { 116 | // EN: The client code may have some of the subsystem's objects 117 | // already created. In this case, it might be worthwhile to 118 | // initialize the Facade with these objects instead of letting the 119 | // Facade create new instances. 120 | // 121 | // RU: В клиентском коде могут быть уже созданы некоторые объекты 122 | // подсистемы. В этом случае может оказаться целесообразным 123 | // инициализировать Фасад с этими объектами вместо того, чтобы 124 | // позволить Фасаду создавать новые экземпляры. 125 | Subsystem1 subsystem1 = new Subsystem1(); 126 | Subsystem2 subsystem2 = new Subsystem2(); 127 | Facade facade = new Facade(subsystem1, subsystem2); 128 | Client.ClientCode(facade); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /FactoryMethod.Conceptual/FactoryMethod.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /FactoryMethod.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | App: Launched with the ConcreteCreator1. 2 | Client: I'm not aware of the creator's class, but it still works. 3 | Creator: The same creator's code has just worked with {Result of ConcreteProduct1} 4 | 5 | App: Launched with the ConcreteCreator2. 6 | Client: I'm not aware of the creator's class, but it still works. 7 | Creator: The same creator's code has just worked with {Result of ConcreteProduct2} -------------------------------------------------------------------------------- /FactoryMethod.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Factory Method Design Pattern 2 | // 3 | // Intent: Provides an interface for creating objects in a superclass, but 4 | // allows subclasses to alter the type of objects that will be created. 5 | // 6 | // RU: Паттерн Фабричный Метод 7 | // 8 | // Назначение: Определяет общий интерфейс для создания объектов в суперклассе, 9 | // позволяя подклассам изменять тип создаваемых объектов. 10 | 11 | using System; 12 | 13 | namespace RefactoringGuru.DesignPatterns.FactoryMethod.Conceptual 14 | { 15 | // EN: The Creator class declares the factory method that is supposed to 16 | // return an object of a Product class. The Creator's subclasses usually 17 | // provide the implementation of this method. 18 | // 19 | // RU: Класс Создатель объявляет фабричный метод, который должен возвращать 20 | // объект класса Продукт. Подклассы Создателя обычно предоставляют 21 | // реализацию этого метода. 22 | abstract class Creator 23 | { 24 | // EN: Note that the Creator may also provide some default 25 | // implementation of the factory method. 26 | // 27 | // RU: Обратите внимание, что Создатель может также обеспечить 28 | // реализацию фабричного метода по умолчанию. 29 | public abstract IProduct FactoryMethod(); 30 | 31 | // EN: Also note that, despite its name, the Creator's primary 32 | // responsibility is not creating products. Usually, it contains some 33 | // core business logic that relies on Product objects, returned by the 34 | // factory method. Subclasses can indirectly change that business logic 35 | // by overriding the factory method and returning a different type of 36 | // product from it. 37 | // 38 | // RU: Также заметьте, что, несмотря на название, основная обязанность 39 | // Создателя не заключается в создании продуктов. Обычно он содержит 40 | // некоторую базовую бизнес-логику, которая основана на объектах 41 | // Продуктов, возвращаемых фабричным методом. Подклассы могут косвенно 42 | // изменять эту бизнес-логику, переопределяя фабричный метод и возвращая 43 | // из него другой тип продукта. 44 | public string SomeOperation() 45 | { 46 | // EN: Call the factory method to create a Product object. 47 | // 48 | // RU: Вызываем фабричный метод, чтобы получить объект-продукт. 49 | var product = FactoryMethod(); 50 | // EN: Now, use the product. 51 | // 52 | // RU: Далее, работаем с этим продуктом. 53 | var result = "Creator: The same creator's code has just worked with " 54 | + product.Operation(); 55 | 56 | return result; 57 | } 58 | } 59 | 60 | // EN: Concrete Creators override the factory method in order to change the 61 | // resulting product's type. 62 | // 63 | // RU: Конкретные Создатели переопределяют фабричный метод для того, чтобы 64 | // изменить тип результирующего продукта. 65 | class ConcreteCreator1 : Creator 66 | { 67 | // EN: Note that the signature of the method still uses the abstract 68 | // product type, even though the concrete product is actually returned 69 | // from the method. This way the Creator can stay independent of 70 | // concrete product classes. 71 | // 72 | // RU: Обратите внимание, что сигнатура метода по-прежнему использует 73 | // тип абстрактного продукта, хотя фактически из метода возвращается 74 | // конкретный продукт. Таким образом, Создатель может оставаться 75 | // независимым от конкретных классов продуктов. 76 | public override IProduct FactoryMethod() 77 | { 78 | return new ConcreteProduct1(); 79 | } 80 | } 81 | 82 | class ConcreteCreator2 : Creator 83 | { 84 | public override IProduct FactoryMethod() 85 | { 86 | return new ConcreteProduct2(); 87 | } 88 | } 89 | 90 | // EN: The Product interface declares the operations that all concrete 91 | // products must implement. 92 | // 93 | // RU: Интерфейс Продукта объявляет операции, которые должны выполнять все 94 | // конкретные продукты. 95 | public interface IProduct 96 | { 97 | string Operation(); 98 | } 99 | 100 | // EN: Concrete Products provide various implementations of the Product 101 | // interface. 102 | // 103 | // RU: Конкретные Продукты предоставляют различные реализации интерфейса 104 | // Продукта. 105 | class ConcreteProduct1 : IProduct 106 | { 107 | public string Operation() 108 | { 109 | return "{Result of ConcreteProduct1}"; 110 | } 111 | } 112 | 113 | class ConcreteProduct2 : IProduct 114 | { 115 | public string Operation() 116 | { 117 | return "{Result of ConcreteProduct2}"; 118 | } 119 | } 120 | 121 | class Client 122 | { 123 | public void Main() 124 | { 125 | Console.WriteLine("App: Launched with the ConcreteCreator1."); 126 | ClientCode(new ConcreteCreator1()); 127 | 128 | Console.WriteLine(""); 129 | 130 | Console.WriteLine("App: Launched with the ConcreteCreator2."); 131 | ClientCode(new ConcreteCreator2()); 132 | } 133 | 134 | // EN: The client code works with an instance of a concrete creator, 135 | // albeit through its base interface. As long as the client keeps 136 | // working with the creator via the base interface, you can pass it any 137 | // creator's subclass. 138 | // 139 | // RU: Клиентский код работает с экземпляром конкретного создателя, хотя 140 | // и через его базовый интерфейс. Пока клиент продолжает работать с 141 | // создателем через базовый интерфейс, вы можете передать ему любой 142 | // подкласс создателя. 143 | public void ClientCode(Creator creator) 144 | { 145 | // ... 146 | Console.WriteLine("Client: I'm not aware of the creator's class," + 147 | "but it still works.\n" + creator.SomeOperation()); 148 | // ... 149 | } 150 | } 151 | 152 | class Program 153 | { 154 | static void Main(string[] args) 155 | { 156 | new Client().Main(); 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /Flyweight.Conceptual/Flyweight.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Flyweight.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | FlyweightFactory: I have 5 flyweights: 2 | Camaro2018_Chevrolet_pink 3 | black_C300_Mercedes Benz 4 | C500_Mercedes Benz_red 5 | BMW_M5_red 6 | BMW_white_X6 7 | 8 | Client: Adding a car to database. 9 | FlyweightFactory: Reusing existing flyweight. 10 | Flyweight: Displaying shared {"Owner":null,"Number":null,"Company":"BMW","Model":"M5","Color":"red"} and unique {"Owner":"James Doe","Number":"CL234IR","Company":"BMW","Model":"M5","Color":"red"} state. 11 | 12 | Client: Adding a car to database. 13 | FlyweightFactory: Can't find a flyweight, creating new one. 14 | Flyweight: Displaying shared {"Owner":null,"Number":null,"Company":"BMW","Model":"X1","Color":"red"} and unique {"Owner":"James Doe","Number":"CL234IR","Company":"BMW","Model":"X1","Color":"red"} state. 15 | 16 | FlyweightFactory: I have 6 flyweights: 17 | Camaro2018_Chevrolet_pink 18 | black_C300_Mercedes Benz 19 | C500_Mercedes Benz_red 20 | BMW_M5_red 21 | BMW_white_X6 22 | BMW_red_X1 -------------------------------------------------------------------------------- /Flyweight.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Flyweight Design Pattern 2 | // 3 | // Intent: Lets you fit more objects into the available amount of RAM by sharing 4 | // common parts of state between multiple objects, instead of keeping all of the 5 | // data in each object. 6 | // 7 | // RU: Паттерн Легковес 8 | // 9 | // Назначение: Позволяет вместить бóльшее количество объектов в отведённую 10 | // оперативную память. Легковес экономит память, разделяя общее состояние 11 | // объектов между собой, вместо хранения одинаковых данных в каждом объекте. 12 | 13 | using System; 14 | using System.Collections.Generic; 15 | using System.Linq; 16 | using System.Text.Json; 17 | // EN: Use Json.NET library, you can download it from NuGet Package Manager 18 | // 19 | // RU: Используем библиотеку Json.NET, загрузить можно через NuGet Package 20 | // Manager 21 | 22 | namespace RefactoringGuru.DesignPatterns.Flyweight.Conceptual 23 | { 24 | // EN: The Flyweight stores a common portion of the state (also called 25 | // intrinsic state) that belongs to multiple real business entities. The 26 | // Flyweight accepts the rest of the state (extrinsic state, unique for each 27 | // entity) via its method parameters. 28 | // 29 | // RU: Легковес хранит общую часть состояния (также называемую внутренним 30 | // состоянием), которая принадлежит нескольким реальным бизнес-объектам. 31 | // Легковес принимает оставшуюся часть состояния (внешнее состояние, 32 | // уникальное для каждого объекта) через его параметры метода. 33 | public class Flyweight 34 | { 35 | private Car _sharedState; 36 | 37 | public Flyweight(Car car) 38 | { 39 | this._sharedState = car; 40 | } 41 | 42 | public void Operation(Car uniqueState) 43 | { 44 | string s = JsonSerializer.Serialize(this._sharedState); 45 | string u = JsonSerializer.Serialize(uniqueState); 46 | Console.WriteLine($"Flyweight: Displaying shared {s} and unique {u} state."); 47 | } 48 | } 49 | 50 | // EN: The Flyweight Factory creates and manages the Flyweight objects. It 51 | // ensures that flyweights are shared correctly. When the client requests a 52 | // flyweight, the factory either returns an existing instance or creates a 53 | // new one, if it doesn't exist yet. 54 | // 55 | // RU: Фабрика Легковесов создает объекты-Легковесы и управляет ими. Она 56 | // обеспечивает правильное разделение легковесов. Когда клиент запрашивает 57 | // легковес, фабрика либо возвращает существующий экземпляр, либо создает 58 | // новый, если он ещё не существует. 59 | public class FlyweightFactory 60 | { 61 | private List> flyweights = new List>(); 62 | 63 | public FlyweightFactory(params Car[] args) 64 | { 65 | foreach (var elem in args) 66 | { 67 | flyweights.Add(new Tuple(new Flyweight(elem), this.getKey(elem))); 68 | } 69 | } 70 | 71 | // EN: Returns a Flyweight's string hash for a given state. 72 | // 73 | // RU: Возвращает хеш строки Легковеса для данного состояния. 74 | public string getKey(Car key) 75 | { 76 | List elements = new List(); 77 | 78 | elements.Add(key.Model); 79 | elements.Add(key.Color); 80 | elements.Add(key.Company); 81 | 82 | if (key.Owner != null && key.Number != null) 83 | { 84 | elements.Add(key.Number); 85 | elements.Add(key.Owner); 86 | } 87 | 88 | elements.Sort(); 89 | 90 | return string.Join("_", elements); 91 | } 92 | 93 | // EN: Returns an existing Flyweight with a given state or creates a new 94 | // one. 95 | // 96 | // RU: Возвращает существующий Легковес с заданным состоянием или 97 | // создает новый. 98 | public Flyweight GetFlyweight(Car sharedState) 99 | { 100 | string key = this.getKey(sharedState); 101 | 102 | if (flyweights.Where(t => t.Item2 == key).Count() == 0) 103 | { 104 | Console.WriteLine("FlyweightFactory: Can't find a flyweight, creating new one."); 105 | this.flyweights.Add(new Tuple(new Flyweight(sharedState), key)); 106 | } 107 | else 108 | { 109 | Console.WriteLine("FlyweightFactory: Reusing existing flyweight."); 110 | } 111 | return this.flyweights.Where(t => t.Item2 == key).FirstOrDefault().Item1; 112 | } 113 | 114 | public void listFlyweights() 115 | { 116 | var count = flyweights.Count; 117 | Console.WriteLine($"\nFlyweightFactory: I have {count} flyweights:"); 118 | foreach (var flyweight in flyweights) 119 | { 120 | Console.WriteLine(flyweight.Item2); 121 | } 122 | } 123 | } 124 | 125 | public class Car 126 | { 127 | public string Owner { get; set; } 128 | 129 | public string Number { get; set; } 130 | 131 | public string Company { get; set; } 132 | 133 | public string Model { get; set; } 134 | 135 | public string Color { get; set; } 136 | } 137 | 138 | class Program 139 | { 140 | static void Main(string[] args) 141 | { 142 | // EN: The client code usually creates a bunch of pre-populated 143 | // flyweights in the initialization stage of the application. 144 | // 145 | // RU: Клиентский код обычно создает кучу предварительно заполненных 146 | // легковесов на этапе инициализации приложения. 147 | var factory = new FlyweightFactory( 148 | new Car { Company = "Chevrolet", Model = "Camaro2018", Color = "pink" }, 149 | new Car { Company = "Mercedes Benz", Model = "C300", Color = "black" }, 150 | new Car { Company = "Mercedes Benz", Model = "C500", Color = "red" }, 151 | new Car { Company = "BMW", Model = "M5", Color = "red" }, 152 | new Car { Company = "BMW", Model = "X6", Color = "white" } 153 | ); 154 | factory.listFlyweights(); 155 | 156 | addCarToPoliceDatabase(factory, new Car { 157 | Number = "CL234IR", 158 | Owner = "James Doe", 159 | Company = "BMW", 160 | Model = "M5", 161 | Color = "red" 162 | }); 163 | 164 | addCarToPoliceDatabase(factory, new Car { 165 | Number = "CL234IR", 166 | Owner = "James Doe", 167 | Company = "BMW", 168 | Model = "X1", 169 | Color = "red" 170 | }); 171 | 172 | factory.listFlyweights(); 173 | } 174 | 175 | public static void addCarToPoliceDatabase(FlyweightFactory factory, Car car) 176 | { 177 | Console.WriteLine("\nClient: Adding a car to database."); 178 | 179 | var flyweight = factory.GetFlyweight(new Car { 180 | Color = car.Color, 181 | Model = car.Model, 182 | Company = car.Company 183 | }); 184 | 185 | // EN: The client code either stores or calculates extrinsic state 186 | // and passes it to the flyweight's methods. 187 | // 188 | // RU: Клиентский код либо сохраняет, либо вычисляет внешнее 189 | // состояние и передает его методам легковеса. 190 | flyweight.Operation(car); 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Iterator.Conceptual/Iterator.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Iterator.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Straight traversal: 2 | First 3 | Second 4 | Third 5 | 6 | Reverse traversal: 7 | Third 8 | Second 9 | First -------------------------------------------------------------------------------- /Iterator.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Iterator Design Pattern 2 | // 3 | // Intent: Lets you traverse elements of a collection without exposing its 4 | // underlying representation (list, stack, tree, etc.). 5 | // 6 | // RU: Паттерн Итератор 7 | // 8 | // Назначение: Даёт возможность последовательно обходить элементы составных 9 | // объектов, не раскрывая их внутреннего представления. 10 | 11 | using System; 12 | using System.Collections; 13 | using System.Collections.Generic; 14 | 15 | namespace RefactoringGuru.DesignPatterns.Iterator.Conceptual 16 | { 17 | abstract class Iterator : IEnumerator 18 | { 19 | object IEnumerator.Current => Current(); 20 | 21 | // EN: Returns the key of the current element 22 | // 23 | // RU: Возвращает ключ текущего элемента 24 | public abstract int Key(); 25 | 26 | // EN: Returns the current element 27 | // 28 | // RU: Возвращает текущий элемент. 29 | public abstract object Current(); 30 | 31 | // EN: Move forward to next element 32 | // 33 | // RU: Переходит к следующему элементу. 34 | public abstract bool MoveNext(); 35 | 36 | // EN: Rewinds the Iterator to the first element 37 | // 38 | // RU: Перематывает Итератор к первому элементу. 39 | public abstract void Reset(); 40 | } 41 | 42 | abstract class IteratorAggregate : IEnumerable 43 | { 44 | // EN: Returns an Iterator or another IteratorAggregate for the 45 | // implementing object. 46 | // 47 | // RU: Возвращает Iterator или другой IteratorAggregate для реализующего 48 | // объекта. 49 | public abstract IEnumerator GetEnumerator(); 50 | } 51 | 52 | // EN: Concrete Iterators implement various traversal algorithms. These 53 | // classes store the current traversal position at all times. 54 | // 55 | // RU: Конкретные Итераторы реализуют различные алгоритмы обхода. Эти классы 56 | // постоянно хранят текущее положение обхода. 57 | class AlphabeticalOrderIterator : Iterator 58 | { 59 | private WordsCollection _collection; 60 | 61 | // EN: Stores the current traversal position. An iterator may have a lot 62 | // of other fields for storing iteration state, especially when it is 63 | // supposed to work with a particular kind of collection. 64 | // 65 | // RU: Хранит текущее положение обхода. У итератора может быть множество 66 | // других полей для хранения состояния итерации, особенно когда он 67 | // должен работать с определённым типом коллекции. 68 | private int _position = -1; 69 | 70 | private bool _reverse = false; 71 | 72 | public AlphabeticalOrderIterator(WordsCollection collection, bool reverse = false) 73 | { 74 | this._collection = collection; 75 | this._reverse = reverse; 76 | 77 | if (reverse) 78 | { 79 | this._position = collection.getItems().Count; 80 | } 81 | } 82 | 83 | public override object Current() 84 | { 85 | return this._collection.getItems()[_position]; 86 | } 87 | 88 | public override int Key() 89 | { 90 | return this._position; 91 | } 92 | 93 | public override bool MoveNext() 94 | { 95 | int updatedPosition = this._position + (this._reverse ? -1 : 1); 96 | 97 | if (updatedPosition >= 0 && updatedPosition < this._collection.getItems().Count) 98 | { 99 | this._position = updatedPosition; 100 | return true; 101 | } 102 | else 103 | { 104 | return false; 105 | } 106 | } 107 | 108 | public override void Reset() 109 | { 110 | this._position = this._reverse ? this._collection.getItems().Count - 1 : 0; 111 | } 112 | } 113 | 114 | // EN: Concrete Collections provide one or several methods for retrieving 115 | // fresh iterator instances, compatible with the collection class. 116 | // 117 | // RU: Конкретные Коллекции предоставляют один или несколько методов для 118 | // получения новых экземпляров итератора, совместимых с классом коллекции. 119 | class WordsCollection : IteratorAggregate 120 | { 121 | List _collection = new List(); 122 | 123 | bool _direction = false; 124 | 125 | public void ReverseDirection() 126 | { 127 | _direction = !_direction; 128 | } 129 | 130 | public List getItems() 131 | { 132 | return _collection; 133 | } 134 | 135 | public void AddItem(string item) 136 | { 137 | this._collection.Add(item); 138 | } 139 | 140 | public override IEnumerator GetEnumerator() 141 | { 142 | return new AlphabeticalOrderIterator(this, _direction); 143 | } 144 | } 145 | 146 | class Program 147 | { 148 | static void Main(string[] args) 149 | { 150 | // EN: The client code may or may not know about the Concrete 151 | // Iterator or Collection classes, depending on the level of 152 | // indirection you want to keep in your program. 153 | // 154 | // RU: Клиентский код может знать или не знать о Конкретном 155 | // Итераторе или классах Коллекций, в зависимости от уровня 156 | // косвенности, который вы хотите сохранить в своей программе. 157 | var collection = new WordsCollection(); 158 | collection.AddItem("First"); 159 | collection.AddItem("Second"); 160 | collection.AddItem("Third"); 161 | 162 | Console.WriteLine("Straight traversal:"); 163 | 164 | foreach (var element in collection) 165 | { 166 | Console.WriteLine(element); 167 | } 168 | 169 | Console.WriteLine("\nReverse traversal:"); 170 | 171 | collection.ReverseDirection(); 172 | 173 | foreach (var element in collection) 174 | { 175 | Console.WriteLine(element); 176 | } 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Refactoring.Guru: Design Patterns in C# 2 | © Alexander Shvets 3 | 4 | This work is licensed under a 5 | Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 6 | 7 | You should have received a copy of the license along with this 8 | work. If not, see . 9 | 10 | ======================================================================= 11 | 12 | Attribution-NonCommercial-NoDerivatives 4.0 International 13 | 14 | ======================================================================= 15 | 16 | Creative Commons Corporation ("Creative Commons") is not a law firm and 17 | does not provide legal services or legal advice. Distribution of 18 | Creative Commons public licenses does not create a lawyer-client or 19 | other relationship. Creative Commons makes its licenses and related 20 | information available on an "as-is" basis. Creative Commons gives no 21 | warranties regarding its licenses, any material licensed under their 22 | terms and conditions, or any related information. Creative Commons 23 | disclaims all liability for damages resulting from their use to the 24 | fullest extent possible. 25 | 26 | Using Creative Commons Public Licenses 27 | 28 | Creative Commons public licenses provide a standard set of terms and 29 | conditions that creators and other rights holders may use to share 30 | original works of authorship and other material subject to copyright 31 | and certain other rights specified in the public license below. The 32 | following considerations are for informational purposes only, are not 33 | exhaustive, and do not form part of our licenses. 34 | 35 | Considerations for licensors: Our public licenses are 36 | intended for use by those authorized to give the public 37 | permission to use material in ways otherwise restricted by 38 | copyright and certain other rights. Our licenses are 39 | irrevocable. Licensors should read and understand the terms 40 | and conditions of the license they choose before applying it. 41 | Licensors should also secure all rights necessary before 42 | applying our licenses so that the public can reuse the 43 | material as expected. Licensors should clearly mark any 44 | material not subject to the license. This includes other CC- 45 | licensed material, or material used under an exception or 46 | limitation to copyright. More considerations for licensors: 47 | wiki.creativecommons.org/Considerations_for_licensors 48 | 49 | Considerations for the public: By using one of our public 50 | licenses, a licensor grants the public permission to use the 51 | licensed material under specified terms and conditions. If 52 | the licensor's permission is not necessary for any reason--for 53 | example, because of any applicable exception or limitation to 54 | copyright--then that use is not regulated by the license. Our 55 | licenses grant only permissions under copyright and certain 56 | other rights that a licensor has authority to grant. Use of 57 | the licensed material may still be restricted for other 58 | reasons, including because others have copyright or other 59 | rights in the material. A licensor may make special requests, 60 | such as asking that all changes be marked or described. 61 | Although not required by our licenses, you are encouraged to 62 | respect those requests where reasonable. More_considerations 63 | for the public: 64 | wiki.creativecommons.org/Considerations_for_licensees 65 | 66 | ======================================================================= 67 | 68 | Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 69 | International Public License 70 | 71 | By exercising the Licensed Rights (defined below), You accept and agree 72 | to be bound by the terms and conditions of this Creative Commons 73 | Attribution-NonCommercial-NoDerivatives 4.0 International Public 74 | License ("Public License"). To the extent this Public License may be 75 | interpreted as a contract, You are granted the Licensed Rights in 76 | consideration of Your acceptance of these terms and conditions, and the 77 | Licensor grants You such rights in consideration of benefits the 78 | Licensor receives from making the Licensed Material available under 79 | these terms and conditions. 80 | 81 | 82 | Section 1 -- Definitions. 83 | 84 | a. Adapted Material means material subject to Copyright and Similar 85 | Rights that is derived from or based upon the Licensed Material 86 | and in which the Licensed Material is translated, altered, 87 | arranged, transformed, or otherwise modified in a manner requiring 88 | permission under the Copyright and Similar Rights held by the 89 | Licensor. For purposes of this Public License, where the Licensed 90 | Material is a musical work, performance, or sound recording, 91 | Adapted Material is always produced where the Licensed Material is 92 | synched in timed relation with a moving image. 93 | 94 | b. Copyright and Similar Rights means copyright and/or similar rights 95 | closely related to copyright including, without limitation, 96 | performance, broadcast, sound recording, and Sui Generis Database 97 | Rights, without regard to how the rights are labeled or 98 | categorized. For purposes of this Public License, the rights 99 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 100 | Rights. 101 | 102 | c. Effective Technological Measures means those measures that, in the 103 | absence of proper authority, may not be circumvented under laws 104 | fulfilling obligations under Article 11 of the WIPO Copyright 105 | Treaty adopted on December 20, 1996, and/or similar international 106 | agreements. 107 | 108 | d. Exceptions and Limitations means fair use, fair dealing, and/or 109 | any other exception or limitation to Copyright and Similar Rights 110 | that applies to Your use of the Licensed Material. 111 | 112 | e. Licensed Material means the artistic or literary work, database, 113 | or other material to which the Licensor applied this Public 114 | License. 115 | 116 | f. Licensed Rights means the rights granted to You subject to the 117 | terms and conditions of this Public License, which are limited to 118 | all Copyright and Similar Rights that apply to Your use of the 119 | Licensed Material and that the Licensor has authority to license. 120 | 121 | g. Licensor means the individual(s) or entity(ies) granting rights 122 | under this Public License. 123 | 124 | h. NonCommercial means not primarily intended for or directed towards 125 | commercial advantage or monetary compensation. For purposes of 126 | this Public License, the exchange of the Licensed Material for 127 | other material subject to Copyright and Similar Rights by digital 128 | file-sharing or similar means is NonCommercial provided there is 129 | no payment of monetary compensation in connection with the 130 | exchange. 131 | 132 | i. Share means to provide material to the public by any means or 133 | process that requires permission under the Licensed Rights, such 134 | as reproduction, public display, public performance, distribution, 135 | dissemination, communication, or importation, and to make material 136 | available to the public including in ways that members of the 137 | public may access the material from a place and at a time 138 | individually chosen by them. 139 | 140 | j. Sui Generis Database Rights means rights other than copyright 141 | resulting from Directive 96/9/EC of the European Parliament and of 142 | the Council of 11 March 1996 on the legal protection of databases, 143 | as amended and/or succeeded, as well as other essentially 144 | equivalent rights anywhere in the world. 145 | 146 | k. You means the individual or entity exercising the Licensed Rights 147 | under this Public License. Your has a corresponding meaning. 148 | 149 | 150 | Section 2 -- Scope. 151 | 152 | a. License grant. 153 | 154 | 1. Subject to the terms and conditions of this Public License, 155 | the Licensor hereby grants You a worldwide, royalty-free, 156 | non-sublicensable, non-exclusive, irrevocable license to 157 | exercise the Licensed Rights in the Licensed Material to: 158 | 159 | a. reproduce and Share the Licensed Material, in whole or 160 | in part, for NonCommercial purposes only; and 161 | 162 | b. produce and reproduce, but not Share, Adapted Material 163 | for NonCommercial purposes only. 164 | 165 | 2. Exceptions and Limitations. For the avoidance of doubt, where 166 | Exceptions and Limitations apply to Your use, this Public 167 | License does not apply, and You do not need to comply with 168 | its terms and conditions. 169 | 170 | 3. Term. The term of this Public License is specified in Section 171 | 6(a). 172 | 173 | 4. Media and formats; technical modifications allowed. The 174 | Licensor authorizes You to exercise the Licensed Rights in 175 | all media and formats whether now known or hereafter created, 176 | and to make technical modifications necessary to do so. The 177 | Licensor waives and/or agrees not to assert any right or 178 | authority to forbid You from making technical modifications 179 | necessary to exercise the Licensed Rights, including 180 | technical modifications necessary to circumvent Effective 181 | Technological Measures. For purposes of this Public License, 182 | simply making modifications authorized by this Section 2(a) 183 | (4) never produces Adapted Material. 184 | 185 | 5. Downstream recipients. 186 | 187 | a. Offer from the Licensor -- Licensed Material. Every 188 | recipient of the Licensed Material automatically 189 | receives an offer from the Licensor to exercise the 190 | Licensed Rights under the terms and conditions of this 191 | Public License. 192 | 193 | b. No downstream restrictions. You may not offer or impose 194 | any additional or different terms or conditions on, or 195 | apply any Effective Technological Measures to, the 196 | Licensed Material if doing so restricts exercise of the 197 | Licensed Rights by any recipient of the Licensed 198 | Material. 199 | 200 | 6. No endorsement. Nothing in this Public License constitutes or 201 | may be construed as permission to assert or imply that You 202 | are, or that Your use of the Licensed Material is, connected 203 | with, or sponsored, endorsed, or granted official status by, 204 | the Licensor or others designated to receive attribution as 205 | provided in Section 3(a)(1)(A)(i). 206 | 207 | b. Other rights. 208 | 209 | 1. Moral rights, such as the right of integrity, are not 210 | licensed under this Public License, nor are publicity, 211 | privacy, and/or other similar personality rights; however, to 212 | the extent possible, the Licensor waives and/or agrees not to 213 | assert any such rights held by the Licensor to the limited 214 | extent necessary to allow You to exercise the Licensed 215 | Rights, but not otherwise. 216 | 217 | 2. Patent and trademark rights are not licensed under this 218 | Public License. 219 | 220 | 3. To the extent possible, the Licensor waives any right to 221 | collect royalties from You for the exercise of the Licensed 222 | Rights, whether directly or through a collecting society 223 | under any voluntary or waivable statutory or compulsory 224 | licensing scheme. In all other cases the Licensor expressly 225 | reserves any right to collect such royalties, including when 226 | the Licensed Material is used other than for NonCommercial 227 | purposes. 228 | 229 | 230 | Section 3 -- License Conditions. 231 | 232 | Your exercise of the Licensed Rights is expressly made subject to the 233 | following conditions. 234 | 235 | a. Attribution. 236 | 237 | 1. If You Share the Licensed Material, You must: 238 | 239 | a. retain the following if it is supplied by the Licensor 240 | with the Licensed Material: 241 | 242 | i. identification of the creator(s) of the Licensed 243 | Material and any others designated to receive 244 | attribution, in any reasonable manner requested by 245 | the Licensor (including by pseudonym if 246 | designated); 247 | 248 | ii. a copyright notice; 249 | 250 | iii. a notice that refers to this Public License; 251 | 252 | iv. a notice that refers to the disclaimer of 253 | warranties; 254 | 255 | v. a URI or hyperlink to the Licensed Material to the 256 | extent reasonably practicable; 257 | 258 | b. indicate if You modified the Licensed Material and 259 | retain an indication of any previous modifications; and 260 | 261 | c. indicate the Licensed Material is licensed under this 262 | Public License, and include the text of, or the URI or 263 | hyperlink to, this Public License. 264 | 265 | For the avoidance of doubt, You do not have permission under 266 | this Public License to Share Adapted Material. 267 | 268 | 2. You may satisfy the conditions in Section 3(a)(1) in any 269 | reasonable manner based on the medium, means, and context in 270 | which You Share the Licensed Material. For example, it may be 271 | reasonable to satisfy the conditions by providing a URI or 272 | hyperlink to a resource that includes the required 273 | information. 274 | 275 | 3. If requested by the Licensor, You must remove any of the 276 | information required by Section 3(a)(1)(A) to the extent 277 | reasonably practicable. 278 | 279 | 280 | Section 4 -- Sui Generis Database Rights. 281 | 282 | Where the Licensed Rights include Sui Generis Database Rights that 283 | apply to Your use of the Licensed Material: 284 | 285 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 286 | to extract, reuse, reproduce, and Share all or a substantial 287 | portion of the contents of the database for NonCommercial purposes 288 | only and provided You do not Share Adapted Material; 289 | 290 | b. if You include all or a substantial portion of the database 291 | contents in a database in which You have Sui Generis Database 292 | Rights, then the database in which You have Sui Generis Database 293 | Rights (but not its individual contents) is Adapted Material; and 294 | 295 | c. You must comply with the conditions in Section 3(a) if You Share 296 | all or a substantial portion of the contents of the database. 297 | 298 | For the avoidance of doubt, this Section 4 supplements and does not 299 | replace Your obligations under this Public License where the Licensed 300 | Rights include other Copyright and Similar Rights. 301 | 302 | 303 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 304 | 305 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 306 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 307 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 308 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 309 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 310 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 311 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 312 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 313 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 314 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 315 | 316 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 317 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 318 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 319 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 320 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 321 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 322 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 323 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 324 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 325 | 326 | c. The disclaimer of warranties and limitation of liability provided 327 | above shall be interpreted in a manner that, to the extent 328 | possible, most closely approximates an absolute disclaimer and 329 | waiver of all liability. 330 | 331 | 332 | Section 6 -- Term and Termination. 333 | 334 | a. This Public License applies for the term of the Copyright and 335 | Similar Rights licensed here. However, if You fail to comply with 336 | this Public License, then Your rights under this Public License 337 | terminate automatically. 338 | 339 | b. Where Your right to use the Licensed Material has terminated under 340 | Section 6(a), it reinstates: 341 | 342 | 1. automatically as of the date the violation is cured, provided 343 | it is cured within 30 days of Your discovery of the 344 | violation; or 345 | 346 | 2. upon express reinstatement by the Licensor. 347 | 348 | For the avoidance of doubt, this Section 6(b) does not affect any 349 | right the Licensor may have to seek remedies for Your violations 350 | of this Public License. 351 | 352 | c. For the avoidance of doubt, the Licensor may also offer the 353 | Licensed Material under separate terms or conditions or stop 354 | distributing the Licensed Material at any time; however, doing so 355 | will not terminate this Public License. 356 | 357 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 358 | License. 359 | 360 | 361 | Section 7 -- Other Terms and Conditions. 362 | 363 | a. The Licensor shall not be bound by any additional or different 364 | terms or conditions communicated by You unless expressly agreed. 365 | 366 | b. Any arrangements, understandings, or agreements regarding the 367 | Licensed Material not stated herein are separate from and 368 | independent of the terms and conditions of this Public License. 369 | 370 | 371 | Section 8 -- Interpretation. 372 | 373 | a. For the avoidance of doubt, this Public License does not, and 374 | shall not be interpreted to, reduce, limit, restrict, or impose 375 | conditions on any use of the Licensed Material that could lawfully 376 | be made without permission under this Public License. 377 | 378 | b. To the extent possible, if any provision of this Public License is 379 | deemed unenforceable, it shall be automatically reformed to the 380 | minimum extent necessary to make it enforceable. If the provision 381 | cannot be reformed, it shall be severed from this Public License 382 | without affecting the enforceability of the remaining terms and 383 | conditions. 384 | 385 | c. No term or condition of this Public License will be waived and no 386 | failure to comply consented to unless expressly agreed to by the 387 | Licensor. 388 | 389 | d. Nothing in this Public License constitutes or may be interpreted 390 | as a limitation upon, or waiver of, any privileges and immunities 391 | that apply to the Licensor or You, including from the legal 392 | processes of any jurisdiction or authority. 393 | 394 | ======================================================================= 395 | 396 | Creative Commons is not a party to its public 397 | licenses. Notwithstanding, Creative Commons may elect to apply one of 398 | its public licenses to material it publishes and in those instances 399 | will be considered the “Licensor.” The text of the Creative Commons 400 | public licenses is dedicated to the public domain under the CC0 Public 401 | Domain Dedication. Except for the limited purpose of indicating that 402 | material is shared under a Creative Commons public license or as 403 | otherwise permitted by the Creative Commons policies published at 404 | creativecommons.org/policies, Creative Commons does not authorize the 405 | use of the trademark "Creative Commons" or any other trademark or logo 406 | of Creative Commons without its prior written consent including, 407 | without limitation, in connection with any unauthorized modifications 408 | to any of its public licenses or any other arrangements, 409 | understandings, or agreements concerning use of licensed material. For 410 | the avoidance of doubt, this paragraph does not form part of the 411 | public licenses. 412 | 413 | Creative Commons may be contacted at creativecommons.org. 414 | 415 | -------------------------------------------------------------------------------- /Mediator.Conceptual/Mediator.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Mediator.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Client triggers operation A. 2 | Component 1 does A. 3 | Mediator reacts on A and triggers following operations: 4 | Component 2 does C. 5 | 6 | Client triggers operation D. 7 | Component 2 does D. 8 | Mediator reacts on D and triggers following operations: 9 | Component 1 does B. 10 | Component 2 does C. -------------------------------------------------------------------------------- /Mediator.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Mediator Design Pattern 2 | // 3 | // Intent: Lets you reduce chaotic dependencies between objects. The pattern 4 | // restricts direct communications between the objects and forces them to 5 | // collaborate only via a mediator object. 6 | // 7 | // RU: Паттерн Посредник 8 | // 9 | // Назначение: Позволяет уменьшить связанность множества классов между собой, 10 | // благодаря перемещению этих связей в один класс-посредник. 11 | 12 | using System; 13 | 14 | namespace RefactoringGuru.DesignPatterns.Mediator.Conceptual 15 | { 16 | // EN: The Mediator interface declares a method used by components to notify 17 | // the mediator about various events. The Mediator may react to these events 18 | // and pass the execution to other components. 19 | // 20 | // RU: Интерфейс Посредника предоставляет метод, используемый компонентами 21 | // для уведомления посредника о различных событиях. Посредник может 22 | // реагировать на эти события и передавать исполнение другим компонентам. 23 | public interface IMediator 24 | { 25 | void Notify(object sender, string ev); 26 | } 27 | 28 | // EN: Concrete Mediators implement cooperative behavior by coordinating 29 | // several components. 30 | // 31 | // RU: Конкретные Посредники реализуют совместное поведение, координируя 32 | // отдельные компоненты. 33 | class ConcreteMediator : IMediator 34 | { 35 | private Component1 _component1; 36 | 37 | private Component2 _component2; 38 | 39 | public ConcreteMediator(Component1 component1, Component2 component2) 40 | { 41 | this._component1 = component1; 42 | this._component1.SetMediator(this); 43 | this._component2 = component2; 44 | this._component2.SetMediator(this); 45 | } 46 | 47 | public void Notify(object sender, string ev) 48 | { 49 | if (ev == "A") 50 | { 51 | Console.WriteLine("Mediator reacts on A and triggers following operations:"); 52 | this._component2.DoC(); 53 | } 54 | if (ev == "D") 55 | { 56 | Console.WriteLine("Mediator reacts on D and triggers following operations:"); 57 | this._component1.DoB(); 58 | this._component2.DoC(); 59 | } 60 | } 61 | } 62 | 63 | // EN: The Base Component provides the basic functionality of storing a 64 | // mediator's instance inside component objects. 65 | // 66 | // RU: Базовый Компонент обеспечивает базовую функциональность хранения 67 | // экземпляра посредника внутри объектов компонентов. 68 | class BaseComponent 69 | { 70 | protected IMediator _mediator; 71 | 72 | public BaseComponent(IMediator mediator = null) 73 | { 74 | this._mediator = mediator; 75 | } 76 | 77 | public void SetMediator(IMediator mediator) 78 | { 79 | this._mediator = mediator; 80 | } 81 | } 82 | 83 | // EN: Concrete Components implement various functionality. They don't 84 | // depend on other components. They also don't depend on any concrete 85 | // mediator classes. 86 | // 87 | // RU: Конкретные Компоненты реализуют различную функциональность. Они не 88 | // зависят от других компонентов. Они также не зависят от каких-либо 89 | // конкретных классов посредников. 90 | class Component1 : BaseComponent 91 | { 92 | public void DoA() 93 | { 94 | Console.WriteLine("Component 1 does A."); 95 | 96 | this._mediator.Notify(this, "A"); 97 | } 98 | 99 | public void DoB() 100 | { 101 | Console.WriteLine("Component 1 does B."); 102 | 103 | this._mediator.Notify(this, "B"); 104 | } 105 | } 106 | 107 | class Component2 : BaseComponent 108 | { 109 | public void DoC() 110 | { 111 | Console.WriteLine("Component 2 does C."); 112 | 113 | this._mediator.Notify(this, "C"); 114 | } 115 | 116 | public void DoD() 117 | { 118 | Console.WriteLine("Component 2 does D."); 119 | 120 | this._mediator.Notify(this, "D"); 121 | } 122 | } 123 | 124 | class Program 125 | { 126 | static void Main(string[] args) 127 | { 128 | // EN: The client code. 129 | // 130 | // RU: Клиентский код. 131 | Component1 component1 = new Component1(); 132 | Component2 component2 = new Component2(); 133 | new ConcreteMediator(component1, component2); 134 | 135 | Console.WriteLine("Client triggers operation A."); 136 | component1.DoA(); 137 | 138 | Console.WriteLine(); 139 | 140 | Console.WriteLine("Client triggers operation D."); 141 | component2.DoD(); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Memento.Conceptual/Memento.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Memento.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Originator: My initial state is: Super-duper-super-puper-super. 2 | 3 | Caretaker: Saving Originator's state... 4 | Originator: I'm doing something important. 5 | Originator: and my state has changed to: oGyQIIatlDDWNgYYqJATTmdwnnGZQj 6 | 7 | Caretaker: Saving Originator's state... 8 | Originator: I'm doing something important. 9 | Originator: and my state has changed to: jBtMDDWogzzRJbTTmEwOOhZrjjBULe 10 | 11 | Caretaker: Saving Originator's state... 12 | Originator: I'm doing something important. 13 | Originator: and my state has changed to: exoHyyRkbuuNEXOhhArKccUmexPPHZ 14 | 15 | Caretaker: Here's the list of mementos: 16 | 12.06.2018 15:52:45 / (Super-dup...) 17 | 12.06.2018 15:52:46 / (oGyQIIatl...) 18 | 12.06.2018 15:52:46 / (jBtMDDWog...) 19 | 20 | Client: Now, let's rollback! 21 | 22 | Caretaker: Restoring state to: 12.06.2018 15:52:46 / (jBtMDDWog...) 23 | Originator: My state has changed to: jBtMDDWogzzRJbTTmEwOOhZrjjBULe 24 | 25 | Client: Once more! 26 | 27 | Caretaker: Restoring state to: 12.06.2018 15:52:46 / (oGyQIIatl...) 28 | Originator: My state has changed to: oGyQIIatlDDWNgYYqJATTmdwnnGZQj -------------------------------------------------------------------------------- /Memento.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Memento Design Pattern 2 | // 3 | // Intent: Lets you save and restore the previous state of an object without 4 | // revealing the details of its implementation. 5 | // 6 | // RU: Паттерн Снимок 7 | // 8 | // Назначение: Позволяет делать снимки состояния объектов, не раскрывая 9 | // подробностей их реализации. Затем снимки можно использовать, чтобы 10 | // восстановить прошлое состояние объектов. 11 | 12 | using System; 13 | using System.Collections.Generic; 14 | using System.Linq; 15 | using System.Threading; 16 | 17 | namespace RefactoringGuru.DesignPatterns.Memento.Conceptual 18 | { 19 | // EN: The Originator holds some important state that may change over time. 20 | // It also defines a method for saving the state inside a memento and 21 | // another method for restoring the state from it. 22 | // 23 | // RU: Создатель содержит некоторое важное состояние, которое может со 24 | // временем меняться. Он также объявляет метод сохранения состояния внутри 25 | // снимка и метод восстановления состояния из него. 26 | class Originator 27 | { 28 | // EN: For the sake of simplicity, the originator's state is stored 29 | // inside a single variable. 30 | // 31 | // RU: Для удобства состояние создателя хранится внутри одной 32 | // переменной. 33 | private string _state; 34 | 35 | public Originator(string state) 36 | { 37 | this._state = state; 38 | Console.WriteLine("Originator: My initial state is: " + state); 39 | } 40 | 41 | // EN: The Originator's business logic may affect its internal state. 42 | // Therefore, the client should backup the state before launching 43 | // methods of the business logic via the save() method. 44 | // 45 | // RU: Бизнес-логика Создателя может повлиять на его внутреннее 46 | // состояние. Поэтому клиент должен выполнить резервное копирование 47 | // состояния с помощью метода save перед запуском методов бизнес-логики. 48 | public void DoSomething() 49 | { 50 | Console.WriteLine("Originator: I'm doing something important."); 51 | this._state = this.GenerateRandomString(30); 52 | Console.WriteLine($"Originator: and my state has changed to: {_state}"); 53 | } 54 | 55 | private string GenerateRandomString(int length = 10) 56 | { 57 | string allowedSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 58 | string result = string.Empty; 59 | 60 | while (length > 0) 61 | { 62 | result += allowedSymbols[new Random().Next(0, allowedSymbols.Length)]; 63 | 64 | Thread.Sleep(12); 65 | 66 | length--; 67 | } 68 | 69 | return result; 70 | } 71 | 72 | // EN: Saves the current state inside a memento. 73 | // 74 | // RU: Сохраняет текущее состояние внутри снимка. 75 | public IMemento Save() 76 | { 77 | return new ConcreteMemento(this._state); 78 | } 79 | 80 | // EN: Restores the Originator's state from a memento object. 81 | // 82 | // RU: Восстанавливает состояние Создателя из объекта снимка. 83 | public void Restore(IMemento memento) 84 | { 85 | if (!(memento is ConcreteMemento)) 86 | { 87 | throw new Exception("Unknown memento class " + memento.ToString()); 88 | } 89 | 90 | this._state = memento.GetState(); 91 | Console.Write($"Originator: My state has changed to: {_state}"); 92 | } 93 | } 94 | 95 | // EN: The Memento interface provides a way to retrieve the memento's 96 | // metadata, such as creation date or name. However, it doesn't expose the 97 | // Originator's state. 98 | // 99 | // RU: Интерфейс Снимка предоставляет способ извлечения метаданных снимка, 100 | // таких как дата создания или название. Однако он не раскрывает состояние 101 | // Создателя. 102 | public interface IMemento 103 | { 104 | string GetName(); 105 | 106 | string GetState(); 107 | 108 | DateTime GetDate(); 109 | } 110 | 111 | // EN: The Concrete Memento contains the infrastructure for storing the 112 | // Originator's state. 113 | // 114 | // RU: Конкретный снимок содержит инфраструктуру для хранения состояния 115 | // Создателя. 116 | class ConcreteMemento : IMemento 117 | { 118 | private string _state; 119 | 120 | private DateTime _date; 121 | 122 | public ConcreteMemento(string state) 123 | { 124 | this._state = state; 125 | this._date = DateTime.Now; 126 | } 127 | 128 | // EN: The Originator uses this method when restoring its state. 129 | // 130 | // RU: Создатель использует этот метод, когда восстанавливает своё 131 | // состояние. 132 | public string GetState() 133 | { 134 | return this._state; 135 | } 136 | 137 | // EN: The rest of the methods are used by the Caretaker to display 138 | // metadata. 139 | // 140 | // RU: Остальные методы используются Опекуном для отображения 141 | // метаданных. 142 | public string GetName() 143 | { 144 | return $"{this._date} / ({this._state.Substring(0, 9)})..."; 145 | } 146 | 147 | public DateTime GetDate() 148 | { 149 | return this._date; 150 | } 151 | } 152 | 153 | // EN: The Caretaker doesn't depend on the Concrete Memento class. 154 | // Therefore, it doesn't have access to the originator's state, stored 155 | // inside the memento. It works with all mementos via the base Memento 156 | // interface. 157 | // 158 | // RU: Опекун не зависит от класса Конкретного Снимка. Таким образом, он не 159 | // имеет доступа к состоянию создателя, хранящемуся внутри снимка. Он 160 | // работает со всеми снимками через базовый интерфейс Снимка. 161 | class Caretaker 162 | { 163 | private List _mementos = new List(); 164 | 165 | private Originator _originator = null; 166 | 167 | public Caretaker(Originator originator) 168 | { 169 | this._originator = originator; 170 | } 171 | 172 | public void Backup() 173 | { 174 | Console.WriteLine("\nCaretaker: Saving Originator's state..."); 175 | this._mementos.Add(this._originator.Save()); 176 | } 177 | 178 | public void Undo() 179 | { 180 | if (this._mementos.Count == 0) 181 | { 182 | return; 183 | } 184 | 185 | var memento = this._mementos.Last(); 186 | this._mementos.Remove(memento); 187 | 188 | Console.WriteLine("Caretaker: Restoring state to: " + memento.GetName()); 189 | 190 | try 191 | { 192 | this._originator.Restore(memento); 193 | } 194 | catch (Exception) 195 | { 196 | this.Undo(); 197 | } 198 | } 199 | 200 | public void ShowHistory() 201 | { 202 | Console.WriteLine("Caretaker: Here's the list of mementos:"); 203 | 204 | foreach (var memento in this._mementos) 205 | { 206 | Console.WriteLine(memento.GetName()); 207 | } 208 | } 209 | } 210 | 211 | class Program 212 | { 213 | static void Main(string[] args) 214 | { 215 | // EN: Client code. 216 | // 217 | // RU: Клиентский код. 218 | Originator originator = new Originator("Super-duper-super-puper-super."); 219 | Caretaker caretaker = new Caretaker(originator); 220 | 221 | caretaker.Backup(); 222 | originator.DoSomething(); 223 | 224 | caretaker.Backup(); 225 | originator.DoSomething(); 226 | 227 | caretaker.Backup(); 228 | originator.DoSomething(); 229 | 230 | Console.WriteLine(); 231 | caretaker.ShowHistory(); 232 | 233 | Console.WriteLine("\nClient: Now, let's rollback!\n"); 234 | caretaker.Undo(); 235 | 236 | Console.WriteLine("\n\nClient: Once more!\n"); 237 | caretaker.Undo(); 238 | 239 | Console.WriteLine(); 240 | } 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /Observer.Conceptual/Observer.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Observer.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Subject: Attached an observer. 2 | Subject: Attached an observer. 3 | 4 | Subject: I'm doing something important. 5 | Subject: My state has just changed to: 2 6 | Subject: Notifying observers... 7 | ConcreteObserverA: Reacted to the event. 8 | ConcreteObserverB: Reacted to the event. 9 | 10 | Subject: I'm doing something important. 11 | Subject: My state has just changed to: 1 12 | Subject: Notifying observers... 13 | ConcreteObserverA: Reacted to the event. 14 | Subject: Detached an observer. 15 | 16 | Subject: I'm doing something important. 17 | Subject: My state has just changed to: 5 18 | Subject: Notifying observers... -------------------------------------------------------------------------------- /Observer.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Observer Design Pattern 2 | // 3 | // Intent: Lets you define a subscription mechanism to notify multiple objects 4 | // about any events that happen to the object they're observing. 5 | // 6 | // Note that there's a lot of different terms with similar meaning associated 7 | // with this pattern. Just remember that the Subject is also called the 8 | // Publisher and the Observer is often called the Subscriber and vice versa. 9 | // Also the verbs "observe", "listen" or "track" usually mean the same thing. 10 | // 11 | // RU: Паттерн Наблюдатель 12 | // 13 | // Назначение: Создаёт механизм подписки, позволяющий одним объектам следить и 14 | // реагировать на события, происходящие в других объектах. 15 | // 16 | // Обратите внимание, что существует множество различных терминов с похожими 17 | // значениями, связанных с этим паттерном. Просто помните, что Субъекта также 18 | // называют Издателем, а Наблюдателя часто называют Подписчиком и наоборот. 19 | // Также глаголы «наблюдать», «слушать» или «отслеживать» обычно означают одно и 20 | // то же. 21 | 22 | using System; 23 | using System.Collections.Generic; 24 | using System.Threading; 25 | 26 | namespace RefactoringGuru.DesignPatterns.Observer.Conceptual 27 | { 28 | public interface IObserver 29 | { 30 | // EN: Receive update from subject 31 | // 32 | // RU: Получает обновление от издателя 33 | void Update(ISubject subject); 34 | } 35 | 36 | public interface ISubject 37 | { 38 | // EN: Attach an observer to the subject. 39 | // 40 | // RU: Присоединяет наблюдателя к издателю. 41 | void Attach(IObserver observer); 42 | 43 | // EN: Detach an observer from the subject. 44 | // 45 | // RU: Отсоединяет наблюдателя от издателя. 46 | void Detach(IObserver observer); 47 | 48 | // EN: Notify all observers about an event. 49 | // 50 | // RU: Уведомляет всех наблюдателей о событии. 51 | void Notify(); 52 | } 53 | 54 | // EN: The Subject owns some important state and notifies observers when the 55 | // state changes. 56 | // 57 | // RU: Издатель владеет некоторым важным состоянием и оповещает наблюдателей 58 | // о его изменениях. 59 | public class Subject : ISubject 60 | { 61 | // EN: For the sake of simplicity, the Subject's state, essential to all 62 | // subscribers, is stored in this variable. 63 | // 64 | // RU: Для удобства в этой переменной хранится состояние Издателя, 65 | // необходимое всем подписчикам. 66 | public int State { get; set; } = -0; 67 | 68 | // EN: List of subscribers. In real life, the list of subscribers can be 69 | // stored more comprehensively (categorized by event type, etc.). 70 | // 71 | // RU: Список подписчиков. В реальной жизни список подписчиков может 72 | // храниться в более подробном виде (классифицируется по типу события и 73 | // т.д.) 74 | private List _observers = new List(); 75 | 76 | // EN: The subscription management methods. 77 | // 78 | // RU: Методы управления подпиской. 79 | public void Attach(IObserver observer) 80 | { 81 | Console.WriteLine("Subject: Attached an observer."); 82 | this._observers.Add(observer); 83 | } 84 | 85 | public void Detach(IObserver observer) 86 | { 87 | this._observers.Remove(observer); 88 | Console.WriteLine("Subject: Detached an observer."); 89 | } 90 | 91 | // EN: Trigger an update in each subscriber. 92 | // 93 | // RU: Запуск обновления в каждом подписчике. 94 | public void Notify() 95 | { 96 | Console.WriteLine("Subject: Notifying observers..."); 97 | 98 | foreach (var observer in _observers) 99 | { 100 | observer.Update(this); 101 | } 102 | } 103 | 104 | // EN: Usually, the subscription logic is only a fraction of what a 105 | // Subject can really do. Subjects commonly hold some important business 106 | // logic, that triggers a notification method whenever something 107 | // important is about to happen (or after it). 108 | // 109 | // RU: Обычно логика подписки – только часть того, что делает Издатель. 110 | // Издатели часто содержат некоторую важную бизнес-логику, которая 111 | // запускает метод уведомления всякий раз, когда должно произойти что-то 112 | // важное (или после этого). 113 | public void SomeBusinessLogic() 114 | { 115 | Console.WriteLine("\nSubject: I'm doing something important."); 116 | this.State = new Random().Next(0, 10); 117 | 118 | Thread.Sleep(15); 119 | 120 | Console.WriteLine("Subject: My state has just changed to: " + this.State); 121 | this.Notify(); 122 | } 123 | } 124 | 125 | // EN: Concrete Observers react to the updates issued by the Subject they 126 | // had been attached to. 127 | // 128 | // RU: Конкретные Наблюдатели реагируют на обновления, выпущенные Издателем, 129 | // к которому они прикреплены. 130 | class ConcreteObserverA : IObserver 131 | { 132 | public void Update(ISubject subject) 133 | { 134 | if ((subject as Subject).State < 3) 135 | { 136 | Console.WriteLine("ConcreteObserverA: Reacted to the event."); 137 | } 138 | } 139 | } 140 | 141 | class ConcreteObserverB : IObserver 142 | { 143 | public void Update(ISubject subject) 144 | { 145 | if ((subject as Subject).State == 0 || (subject as Subject).State >= 2) 146 | { 147 | Console.WriteLine("ConcreteObserverB: Reacted to the event."); 148 | } 149 | } 150 | } 151 | 152 | class Program 153 | { 154 | static void Main(string[] args) 155 | { 156 | // EN: The client code. 157 | // 158 | // RU: Клиентский код. 159 | var subject = new Subject(); 160 | var observerA = new ConcreteObserverA(); 161 | subject.Attach(observerA); 162 | 163 | var observerB = new ConcreteObserverB(); 164 | subject.Attach(observerB); 165 | 166 | subject.SomeBusinessLogic(); 167 | subject.SomeBusinessLogic(); 168 | 169 | subject.Detach(observerB); 170 | 171 | subject.SomeBusinessLogic(); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Prototype.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Original values of p1, p2, p3: 2 | p1 instance values: 3 | Name: Jack Daniels, Age: 42, BirthDate: 01/01/77 4 | ID#: 666 5 | p2 instance values: 6 | Name: Jack Daniels, Age: 42, BirthDate: 01/01/77 7 | ID#: 666 8 | p3 instance values: 9 | Name: Jack Daniels, Age: 42, BirthDate: 01/01/77 10 | ID#: 666 11 | 12 | Values of p1, p2 and p3 after changes to p1: 13 | p1 instance values: 14 | Name: Frank, Age: 32, BirthDate: 01/01/00 15 | ID#: 7878 16 | p2 instance values (reference values have changed): 17 | Name: Jack Daniels, Age: 42, BirthDate: 01/01/77 18 | ID#: 7878 19 | p3 instance values (everything was kept the same): 20 | Name: Jack Daniels, Age: 42, BirthDate: 01/01/77 21 | ID#: 666 -------------------------------------------------------------------------------- /Prototype.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Prototype Design Pattern 2 | // 3 | // Intent: Lets you copy existing objects without making your code dependent on 4 | // their classes. 5 | // 6 | // RU: Паттерн Прототип 7 | // 8 | // Назначение: Позволяет копировать объекты, не вдаваясь в подробности их 9 | // реализации. 10 | 11 | using System; 12 | 13 | namespace RefactoringGuru.DesignPatterns.Prototype.Conceptual 14 | { 15 | public class Person 16 | { 17 | public int Age; 18 | public DateTime BirthDate; 19 | public string Name; 20 | public IdInfo IdInfo; 21 | 22 | public Person ShallowCopy() 23 | { 24 | return (Person) this.MemberwiseClone(); 25 | } 26 | 27 | public Person DeepCopy() 28 | { 29 | Person clone = (Person) this.MemberwiseClone(); 30 | clone.IdInfo = new IdInfo(IdInfo.IdNumber); 31 | clone.Name = String.Copy(Name); 32 | return clone; 33 | } 34 | } 35 | 36 | public class IdInfo 37 | { 38 | public int IdNumber; 39 | 40 | public IdInfo(int idNumber) 41 | { 42 | this.IdNumber = idNumber; 43 | } 44 | } 45 | 46 | class Program 47 | { 48 | static void Main(string[] args) 49 | { 50 | Person p1 = new Person(); 51 | p1.Age = 42; 52 | p1.BirthDate = Convert.ToDateTime("1977-01-01"); 53 | p1.Name = "Jack Daniels"; 54 | p1.IdInfo = new IdInfo(666); 55 | 56 | // EN: Perform a shallow copy of p1 and assign it to p2. 57 | // 58 | // RU: Выполнить поверхностное копирование p1 и присвоить её p2. 59 | Person p2 = p1.ShallowCopy(); 60 | // EN: Make a deep copy of p1 and assign it to p3. 61 | // 62 | // RU: Сделать глубокую копию p1 и присвоить её p3. 63 | Person p3 = p1.DeepCopy(); 64 | 65 | // EN: Display values of p1, p2 and p3. 66 | // 67 | // RU: Вывести значения p1, p2 и p3. 68 | Console.WriteLine("Original values of p1, p2, p3:"); 69 | Console.WriteLine(" p1 instance values: "); 70 | DisplayValues(p1); 71 | Console.WriteLine(" p2 instance values:"); 72 | DisplayValues(p2); 73 | Console.WriteLine(" p3 instance values:"); 74 | DisplayValues(p3); 75 | 76 | // EN: Change the value of p1 properties and display the values of 77 | // p1, p2 and p3. 78 | // 79 | // RU: Изменить значение свойств p1 и отобразить значения p1, p2 и 80 | // p3. 81 | p1.Age = 32; 82 | p1.BirthDate = Convert.ToDateTime("1900-01-01"); 83 | p1.Name = "Frank"; 84 | p1.IdInfo.IdNumber = 7878; 85 | Console.WriteLine("\nValues of p1, p2 and p3 after changes to p1:"); 86 | Console.WriteLine(" p1 instance values: "); 87 | DisplayValues(p1); 88 | Console.WriteLine(" p2 instance values (reference values have changed):"); 89 | DisplayValues(p2); 90 | Console.WriteLine(" p3 instance values (everything was kept the same):"); 91 | DisplayValues(p3); 92 | } 93 | 94 | public static void DisplayValues(Person p) 95 | { 96 | Console.WriteLine(" Name: {0:s}, Age: {1:d}, BirthDate: {2:MM/dd/yy}", 97 | p.Name, p.Age, p.BirthDate); 98 | Console.WriteLine(" ID#: {0:d}", p.IdInfo.IdNumber); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Prototype.Conceptual/Prototype.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Proxy.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Client: Executing the client code with a real subject: 2 | RealSubject: Handling Request. 3 | 4 | Client: Executing the same client code with a proxy: 5 | Proxy: Checking access prior to firing a real request. 6 | RealSubject: Handling Request. 7 | Proxy: Logging the time of request. -------------------------------------------------------------------------------- /Proxy.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Proxy Design Pattern 2 | // 3 | // Intent: Lets you provide a substitute or placeholder for another object. A 4 | // proxy controls access to the original object, allowing you to perform 5 | // something either before or after the request gets through to the original 6 | // object. 7 | // 8 | // RU: Паттерн Заместитель 9 | // 10 | // Назначение: Позволяет подставлять вместо реальных объектов специальные 11 | // объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, 12 | // позволяя сделать что-то до или после передачи вызова оригиналу. 13 | 14 | using System; 15 | 16 | namespace RefactoringGuru.DesignPatterns.Proxy.Conceptual 17 | { 18 | // EN: The Subject interface declares common operations for both RealSubject 19 | // and the Proxy. As long as the client works with RealSubject using this 20 | // interface, you'll be able to pass it a proxy instead of a real subject. 21 | // 22 | // RU: Интерфейс Субъекта объявляет общие операции как для Реального 23 | // Субъекта, так и для Заместителя. Пока клиент работает с Реальным 24 | // Субъектом, используя этот интерфейс, вы сможете передать ему заместителя 25 | // вместо реального субъекта. 26 | public interface ISubject 27 | { 28 | void Request(); 29 | } 30 | 31 | // EN: The RealSubject contains some core business logic. Usually, 32 | // RealSubjects are capable of doing some useful work which may also be very 33 | // slow or sensitive - e.g. correcting input data. A Proxy can solve these 34 | // issues without any changes to the RealSubject's code. 35 | // 36 | // RU: Реальный Субъект содержит некоторую базовую бизнес-логику. Как 37 | // правило, Реальные Субъекты способны выполнять некоторую полезную работу, 38 | // которая к тому же может быть очень медленной или точной – например, 39 | // коррекция входных данных. Заместитель может решить эти задачи без 40 | // каких-либо изменений в коде Реального Субъекта. 41 | class RealSubject : ISubject 42 | { 43 | public void Request() 44 | { 45 | Console.WriteLine("RealSubject: Handling Request."); 46 | } 47 | } 48 | 49 | // EN: The Proxy has an interface identical to the RealSubject. 50 | // 51 | // RU: Интерфейс Заместителя идентичен интерфейсу Реального Субъекта. 52 | class Proxy : ISubject 53 | { 54 | private RealSubject _realSubject; 55 | 56 | public Proxy(RealSubject realSubject) 57 | { 58 | this._realSubject = realSubject; 59 | } 60 | 61 | // EN: The most common applications of the Proxy pattern are lazy 62 | // loading, caching, controlling the access, logging, etc. A Proxy can 63 | // perform one of these things and then, depending on the result, pass 64 | // the execution to the same method in a linked RealSubject object. 65 | // 66 | // RU: Наиболее распространёнными областями применения паттерна 67 | // Заместитель являются ленивая загрузка, кэширование, контроль доступа, 68 | // ведение журнала и т.д. Заместитель может выполнить одну из этих 69 | // задач, а затем, в зависимости от результата, передать выполнение 70 | // одноимённому методу в связанном объекте класса Реального Субъект. 71 | public void Request() 72 | { 73 | if (this.CheckAccess()) 74 | { 75 | this._realSubject.Request(); 76 | 77 | this.LogAccess(); 78 | } 79 | } 80 | 81 | public bool CheckAccess() 82 | { 83 | // EN: Some real checks should go here. 84 | // 85 | // RU: Некоторые реальные проверки должны проходить здесь. 86 | Console.WriteLine("Proxy: Checking access prior to firing a real request."); 87 | 88 | return true; 89 | } 90 | 91 | public void LogAccess() 92 | { 93 | Console.WriteLine("Proxy: Logging the time of request."); 94 | } 95 | } 96 | 97 | public class Client 98 | { 99 | // EN: The client code is supposed to work with all objects (both 100 | // subjects and proxies) via the Subject interface in order to support 101 | // both real subjects and proxies. In real life, however, clients mostly 102 | // work with their real subjects directly. In this case, to implement 103 | // the pattern more easily, you can extend your proxy from the real 104 | // subject's class. 105 | // 106 | // RU: Клиентский код должен работать со всеми объектами (как с 107 | // реальными, так и заместителями) через интерфейс Субъекта, чтобы 108 | // поддерживать как реальные субъекты, так и заместителей. В реальной 109 | // жизни, однако, клиенты в основном работают с реальными субъектами 110 | // напрямую. В этом случае, для более простой реализации паттерна, можно 111 | // расширить заместителя из класса реального субъекта. 112 | public void ClientCode(ISubject subject) 113 | { 114 | // ... 115 | 116 | subject.Request(); 117 | 118 | // ... 119 | } 120 | } 121 | 122 | class Program 123 | { 124 | static void Main(string[] args) 125 | { 126 | Client client = new Client(); 127 | 128 | Console.WriteLine("Client: Executing the client code with a real subject:"); 129 | RealSubject realSubject = new RealSubject(); 130 | client.ClientCode(realSubject); 131 | 132 | Console.WriteLine(); 133 | 134 | Console.WriteLine("Client: Executing the same client code with a proxy:"); 135 | Proxy proxy = new Proxy(realSubject); 136 | client.ClientCode(proxy); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /Proxy.Conceptual/Proxy.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns in C# 2 | 3 | This repository is part of the [Refactoring.Guru](https://refactoring.guru/design-patterns) project. 4 | 5 | It contains C# examples for all classic GoF design patterns. Each pattern includes two examples: 6 | 7 | - [x] **Conceptual** examples show the internal structure of patterns with detailed comments. 8 | - [ ] **RealWorld** examples show how the patterns can be used in a real-world C# application. 9 | 10 | 11 | ## Requirements 12 | 13 | Most examples are console apps built using C# .NET 8.0. 14 | 15 | For the best experience, we recommend working with examples with these IDEs: 16 | 17 | - [Visual Studio 2017 and newer](https://www.visualstudio.com/downloads/) on Windows/Mac. 18 | - [Visual Studio Code](https://code.visualstudio.com/) on any OS. 19 | - [Rider](https://www.jetbrains.com/rider/) on any OS. 20 | 21 | 22 | ## Contributor's Guide 23 | 24 | We appreciate any help, whether it's a simple fix of a typo or a whole new example. Just [make a fork](https://help.github.com/articles/fork-a-repo/), do your change and submit a [pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/). 25 | 26 | Here's a style guide which might help you to keep your changes consistent with our code: 27 | 28 | 1. All code should follow the [Microsoft C# code style guide](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions). 29 | 30 | 2. Try to hard wrap the code at 80th's character. It helps to list the code on the website without scrollbars. 31 | 32 | 3. The actual examples should be represented by projects with the following naming convention: {PatternName}.{ExampleName}. 33 | 34 | 4. The code should have the following namespace: RefactoringGuru.DesignPatterns.{PatternName}.{ExampleName}. 35 | 36 | 5. Aim to put all code within one file. We realize that it's not how it supposed to be done in production. But it helps people to better understand examples, since all code fits into one screen. 37 | 38 | 6. Comments may or may not have language tags in them, such as this: 39 | 40 | ```csharp 41 | // EN: All products families have the same varieties (MacOS/Windows). 42 | // 43 | // This is a MacOS variant of a button. 44 | // 45 | // RU: Все семейства продуктов имеют одни и те же вариации (MacOS/Windows). 46 | // 47 | // Это вариант кнопки под MacOS. 48 | ``` 49 | 50 | This notation helps to keep the code in one place while allowing the website to generates separate versions of examples for all listed languages. Don't be scared and ignore the non-English part of such comments. If you want to change something in a comment like this, just do it. Even if you do it wrong, we'll tell you how to fix it during the Pull Request. 51 | 52 | 53 | ## License 54 | 55 | This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. 56 | 57 | Creative Commons License 58 | 59 | ## Credits 60 | 61 | Authors: Alexander Shvets ([@neochief](https://github.com/neochief)) and Alexey Pyltsyn ([@lex111](https://github.com/lex111)) 62 | -------------------------------------------------------------------------------- /RefactoringGuru.DesignPatterns.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{FCD7129C-B686-4B85-96F1-0CCD4ABFDD9F}" 5 | ProjectSection(SolutionItems) = preProject 6 | LICENSE.txt = LICENSE.txt 7 | README.md = README.md 8 | EndProjectSection 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbstractFactory.Conceptual", "AbstractFactory.Conceptual\AbstractFactory.Conceptual.csproj", "{572B9238-2B81-45B8-B9B3-E8FEF0793384}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FactoryMethod.Conceptual", "FactoryMethod.Conceptual\FactoryMethod.Conceptual.csproj", "{6E87F5CA-A129-45F1-B5D0-42808F381509}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bridge.Conceptual", "Bridge.Conceptual\Bridge.Conceptual.csproj", "{29523C56-CD0C-4B48-8CD6-23517657E140}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Adapter.Conceptual", "Adapter.Conceptual\Adapter.Conceptual.csproj", "{C75BB186-54C4-476B-AE80-2673D150420F}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Builder.Conceptual", "Builder.Conceptual\Builder.Conceptual.csproj", "{8DF3A326-586B-4F45-A124-0CD13A80891C}" 19 | EndProject 20 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChainOfResponsibility.Conceptual", "ChainOfResponsibility.Conceptual\ChainOfResponsibility.Conceptual.csproj", "{9F39FBB3-44A7-419D-9CBF-C9F2E20CC133}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Command.Conceptual", "Command.Conceptual\Command.Conceptual.csproj", "{96D5D6FA-9AE2-43D3-9A84-6C1C36454315}" 23 | EndProject 24 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Composite.Conceptual", "Composite.Conceptual\Composite.Conceptual.csproj", "{D4448182-512A-41C0-8028-9FCE297A4348}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Decorator.Conceptual", "Decorator.Conceptual\Decorator.Conceptual.csproj", "{54EC33DD-F67C-4FB7-977D-A42C7DCDEC5C}" 27 | EndProject 28 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Facade.Conceptual", "Facade.Conceptual\Facade.Conceptual.csproj", "{C79397B9-AB02-4876-94E2-3BDCD50DAAA9}" 29 | EndProject 30 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flyweight.Conceptual", "Flyweight.Conceptual\Flyweight.Conceptual.csproj", "{98D41F67-7F41-4A3B-8076-489C06353365}" 31 | EndProject 32 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Iterator.Conceptual", "Iterator.Conceptual\Iterator.Conceptual.csproj", "{DD465ED6-C11E-4DDD-8E47-0327DE16FC9E}" 33 | EndProject 34 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mediator.Conceptual", "Mediator.Conceptual\Mediator.Conceptual.csproj", "{DDF08C9F-6990-4D97-8140-0515F8E7696B}" 35 | EndProject 36 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Memento.Conceptual", "Memento.Conceptual\Memento.Conceptual.csproj", "{C2D58654-ADCF-499C-AF36-D6BE603C4A80}" 37 | EndProject 38 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Observer.Conceptual", "Observer.Conceptual\Observer.Conceptual.csproj", "{3A6345DD-995C-4226-A1EF-1E9B7AC7155F}" 39 | EndProject 40 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prototype.Conceptual", "Prototype.Conceptual\Prototype.Conceptual.csproj", "{D43D8FC1-7180-422F-BEB5-2EC493B7995B}" 41 | EndProject 42 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proxy.Conceptual", "Proxy.Conceptual\Proxy.Conceptual.csproj", "{422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}" 43 | EndProject 44 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "State.Conceptual", "State.Conceptual\State.Conceptual.csproj", "{111A1AE4-043C-40F5-915F-FC1CD903F751}" 45 | EndProject 46 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Strategy.Conceptual", "Strategy.Conceptual\Strategy.Conceptual.csproj", "{AABC9303-B16F-4803-B5B4-8B7336EA96E8}" 47 | EndProject 48 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplateMethod.Conceptual", "TemplateMethod.Conceptual\TemplateMethod.Conceptual.csproj", "{046C4FCC-509A-439E-980A-BFBC0E62A79F}" 49 | EndProject 50 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Visitor.Conceptual", "Visitor.Conceptual\Visitor.Conceptual.csproj", "{6834BA82-2BAA-485E-BB8F-96078DAC6877}" 51 | 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 56 | Global 57 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 58 | Debug|Any CPU = Debug|Any CPU 59 | Release|Any CPU = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 62 | {572B9238-2B81-45B8-B9B3-E8FEF0793384}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {572B9238-2B81-45B8-B9B3-E8FEF0793384}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {572B9238-2B81-45B8-B9B3-E8FEF0793384}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {572B9238-2B81-45B8-B9B3-E8FEF0793384}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {6E87F5CA-A129-45F1-B5D0-42808F381509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {6E87F5CA-A129-45F1-B5D0-42808F381509}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {6E87F5CA-A129-45F1-B5D0-42808F381509}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {6E87F5CA-A129-45F1-B5D0-42808F381509}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {29523C56-CD0C-4B48-8CD6-23517657E140}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {29523C56-CD0C-4B48-8CD6-23517657E140}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {29523C56-CD0C-4B48-8CD6-23517657E140}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {29523C56-CD0C-4B48-8CD6-23517657E140}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {C75BB186-54C4-476B-AE80-2673D150420F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {C75BB186-54C4-476B-AE80-2673D150420F}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {C75BB186-54C4-476B-AE80-2673D150420F}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {C75BB186-54C4-476B-AE80-2673D150420F}.Release|Any CPU.Build.0 = Release|Any CPU 78 | {8DF3A326-586B-4F45-A124-0CD13A80891C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 79 | {8DF3A326-586B-4F45-A124-0CD13A80891C}.Debug|Any CPU.Build.0 = Debug|Any CPU 80 | {8DF3A326-586B-4F45-A124-0CD13A80891C}.Release|Any CPU.ActiveCfg = Release|Any CPU 81 | {8DF3A326-586B-4F45-A124-0CD13A80891C}.Release|Any CPU.Build.0 = Release|Any CPU 82 | {9F39FBB3-44A7-419D-9CBF-C9F2E20CC133}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 83 | {9F39FBB3-44A7-419D-9CBF-C9F2E20CC133}.Debug|Any CPU.Build.0 = Debug|Any CPU 84 | {9F39FBB3-44A7-419D-9CBF-C9F2E20CC133}.Release|Any CPU.ActiveCfg = Release|Any CPU 85 | {9F39FBB3-44A7-419D-9CBF-C9F2E20CC133}.Release|Any CPU.Build.0 = Release|Any CPU 86 | {96D5D6FA-9AE2-43D3-9A84-6C1C36454315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 87 | {96D5D6FA-9AE2-43D3-9A84-6C1C36454315}.Debug|Any CPU.Build.0 = Debug|Any CPU 88 | {96D5D6FA-9AE2-43D3-9A84-6C1C36454315}.Release|Any CPU.ActiveCfg = Release|Any CPU 89 | {96D5D6FA-9AE2-43D3-9A84-6C1C36454315}.Release|Any CPU.Build.0 = Release|Any CPU 90 | {D4448182-512A-41C0-8028-9FCE297A4348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 91 | {D4448182-512A-41C0-8028-9FCE297A4348}.Debug|Any CPU.Build.0 = Debug|Any CPU 92 | {D4448182-512A-41C0-8028-9FCE297A4348}.Release|Any CPU.ActiveCfg = Release|Any CPU 93 | {D4448182-512A-41C0-8028-9FCE297A4348}.Release|Any CPU.Build.0 = Release|Any CPU 94 | {54EC33DD-F67C-4FB7-977D-A42C7DCDEC5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 95 | {54EC33DD-F67C-4FB7-977D-A42C7DCDEC5C}.Debug|Any CPU.Build.0 = Debug|Any CPU 96 | {54EC33DD-F67C-4FB7-977D-A42C7DCDEC5C}.Release|Any CPU.ActiveCfg = Release|Any CPU 97 | {54EC33DD-F67C-4FB7-977D-A42C7DCDEC5C}.Release|Any CPU.Build.0 = Release|Any CPU 98 | {C79397B9-AB02-4876-94E2-3BDCD50DAAA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 99 | {C79397B9-AB02-4876-94E2-3BDCD50DAAA9}.Debug|Any CPU.Build.0 = Debug|Any CPU 100 | {C79397B9-AB02-4876-94E2-3BDCD50DAAA9}.Release|Any CPU.ActiveCfg = Release|Any CPU 101 | {C79397B9-AB02-4876-94E2-3BDCD50DAAA9}.Release|Any CPU.Build.0 = Release|Any CPU 102 | {98D41F67-7F41-4A3B-8076-489C06353365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 103 | {98D41F67-7F41-4A3B-8076-489C06353365}.Debug|Any CPU.Build.0 = Debug|Any CPU 104 | {98D41F67-7F41-4A3B-8076-489C06353365}.Release|Any CPU.ActiveCfg = Release|Any CPU 105 | {98D41F67-7F41-4A3B-8076-489C06353365}.Release|Any CPU.Build.0 = Release|Any CPU 106 | {DD465ED6-C11E-4DDD-8E47-0327DE16FC9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 107 | {DD465ED6-C11E-4DDD-8E47-0327DE16FC9E}.Debug|Any CPU.Build.0 = Debug|Any CPU 108 | {DD465ED6-C11E-4DDD-8E47-0327DE16FC9E}.Release|Any CPU.ActiveCfg = Release|Any CPU 109 | {DD465ED6-C11E-4DDD-8E47-0327DE16FC9E}.Release|Any CPU.Build.0 = Release|Any CPU 110 | {DDF08C9F-6990-4D97-8140-0515F8E7696B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 111 | {DDF08C9F-6990-4D97-8140-0515F8E7696B}.Debug|Any CPU.Build.0 = Debug|Any CPU 112 | {DDF08C9F-6990-4D97-8140-0515F8E7696B}.Release|Any CPU.ActiveCfg = Release|Any CPU 113 | {DDF08C9F-6990-4D97-8140-0515F8E7696B}.Release|Any CPU.Build.0 = Release|Any CPU 114 | {C2D58654-ADCF-499C-AF36-D6BE603C4A80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 115 | {C2D58654-ADCF-499C-AF36-D6BE603C4A80}.Debug|Any CPU.Build.0 = Debug|Any CPU 116 | {C2D58654-ADCF-499C-AF36-D6BE603C4A80}.Release|Any CPU.ActiveCfg = Release|Any CPU 117 | {C2D58654-ADCF-499C-AF36-D6BE603C4A80}.Release|Any CPU.Build.0 = Release|Any CPU 118 | {3A6345DD-995C-4226-A1EF-1E9B7AC7155F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 119 | {3A6345DD-995C-4226-A1EF-1E9B7AC7155F}.Debug|Any CPU.Build.0 = Debug|Any CPU 120 | {3A6345DD-995C-4226-A1EF-1E9B7AC7155F}.Release|Any CPU.ActiveCfg = Release|Any CPU 121 | {3A6345DD-995C-4226-A1EF-1E9B7AC7155F}.Release|Any CPU.Build.0 = Release|Any CPU 122 | {D43D8FC1-7180-422F-BEB5-2EC493B7995B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 123 | {D43D8FC1-7180-422F-BEB5-2EC493B7995B}.Debug|Any CPU.Build.0 = Debug|Any CPU 124 | {D43D8FC1-7180-422F-BEB5-2EC493B7995B}.Release|Any CPU.ActiveCfg = Release|Any CPU 125 | {D43D8FC1-7180-422F-BEB5-2EC493B7995B}.Release|Any CPU.Build.0 = Release|Any CPU 126 | {422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 127 | {422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}.Debug|Any CPU.Build.0 = Debug|Any CPU 128 | {422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}.Release|Any CPU.ActiveCfg = Release|Any CPU 129 | {422D8542-34CD-4A2D-B4BC-3CFA4A8F6E17}.Release|Any CPU.Build.0 = Release|Any CPU 130 | {111A1AE4-043C-40F5-915F-FC1CD903F751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 131 | {111A1AE4-043C-40F5-915F-FC1CD903F751}.Debug|Any CPU.Build.0 = Debug|Any CPU 132 | {111A1AE4-043C-40F5-915F-FC1CD903F751}.Release|Any CPU.ActiveCfg = Release|Any CPU 133 | {111A1AE4-043C-40F5-915F-FC1CD903F751}.Release|Any CPU.Build.0 = Release|Any CPU 134 | {AABC9303-B16F-4803-B5B4-8B7336EA96E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 135 | {AABC9303-B16F-4803-B5B4-8B7336EA96E8}.Debug|Any CPU.Build.0 = Debug|Any CPU 136 | {AABC9303-B16F-4803-B5B4-8B7336EA96E8}.Release|Any CPU.ActiveCfg = Release|Any CPU 137 | {AABC9303-B16F-4803-B5B4-8B7336EA96E8}.Release|Any CPU.Build.0 = Release|Any CPU 138 | {046C4FCC-509A-439E-980A-BFBC0E62A79F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 139 | {046C4FCC-509A-439E-980A-BFBC0E62A79F}.Debug|Any CPU.Build.0 = Debug|Any CPU 140 | {046C4FCC-509A-439E-980A-BFBC0E62A79F}.Release|Any CPU.ActiveCfg = Release|Any CPU 141 | {046C4FCC-509A-439E-980A-BFBC0E62A79F}.Release|Any CPU.Build.0 = Release|Any CPU 142 | {6834BA82-2BAA-485E-BB8F-96078DAC6877}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 143 | {6834BA82-2BAA-485E-BB8F-96078DAC6877}.Debug|Any CPU.Build.0 = Debug|Any CPU 144 | {6834BA82-2BAA-485E-BB8F-96078DAC6877}.Release|Any CPU.ActiveCfg = Release|Any CPU 145 | {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 154 | EndGlobalSection 155 | GlobalSection(NestedProjects) = preSolution 156 | EndGlobalSection 157 | EndGlobal 158 | -------------------------------------------------------------------------------- /Singleton.Conceptual/NonThreadSafe/Output.txt: -------------------------------------------------------------------------------- 1 | Singleton works, both variables contain the same instance. -------------------------------------------------------------------------------- /Singleton.Conceptual/NonThreadSafe/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Singleton Design Pattern 2 | // 3 | // Intent: Lets you ensure that a class has only one instance, while providing a 4 | // global access point to this instance. 5 | // 6 | // RU: Паттерн Одиночка 7 | // 8 | // Назначение: Гарантирует, что у класса есть только один экземпляр, и 9 | // предоставляет к нему глобальную точку доступа. 10 | 11 | using System; 12 | 13 | namespace RefactoringGuru.DesignPatterns.Singleton.Conceptual.NonThreadSafe 14 | { 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. 18 | // 19 | // RU: Класс Одиночка предоставляет метод `GetInstance`, который ведёт себя 20 | // как альтернативный конструктор и позволяет клиентам получать один и тот 21 | // же экземпляр класса при каждом вызове. 22 | 23 | // EN : The Singleton should always be a 'sealed' class to prevent class 24 | // inheritance through external classes and also through nested classes. 25 | public sealed class Singleton 26 | { 27 | // EN: The Singleton's constructor should always be private to prevent 28 | // direct construction calls with the `new` operator. 29 | // 30 | // RU: Конструктор Одиночки всегда должен быть скрытым, чтобы 31 | // предотвратить создание объекта через оператор new. 32 | private Singleton() { } 33 | 34 | // EN: The Singleton's instance is stored in a static field. There there 35 | // are multiple ways to initialize this field, all of them have various 36 | // pros and cons. In this example we'll show the simplest of these ways, 37 | // which, however, doesn't work really well in multithreaded program. 38 | // 39 | // RU: Объект одиночки храниться в статичном поле класса. Существует 40 | // несколько способов инициализировать это поле, и все они имеют разные 41 | // достоинства и недостатки. В этом примере мы рассмотрим простейший из 42 | // них, недостатком которого является полная неспособность правильно 43 | // работать в многопоточной среде. 44 | private static Singleton _instance; 45 | 46 | // EN: This is the static method that controls the access to the 47 | // singleton instance. On the first run, it creates a singleton object 48 | // and places it into the static field. On subsequent runs, it returns 49 | // the client existing object stored in the static field. 50 | // 51 | // RU: Это статический метод, управляющий доступом к экземпляру 52 | // одиночки. При первом запуске, он создаёт экземпляр одиночки и 53 | // помещает его в статическое поле. При последующих запусках, он 54 | // возвращает клиенту объект, хранящийся в статическом поле. 55 | public static Singleton GetInstance() 56 | { 57 | if (_instance == null) 58 | { 59 | _instance = new Singleton(); 60 | } 61 | return _instance; 62 | } 63 | 64 | // EN: Finally, any singleton should define some business logic, which 65 | // can be executed on its instance. 66 | // 67 | // RU: Наконец, любой одиночка должен содержать некоторую бизнес-логику, 68 | // которая может быть выполнена на его экземпляре. 69 | public void someBusinessLogic() 70 | { 71 | // ... 72 | } 73 | } 74 | 75 | class Program 76 | { 77 | static void Main(string[] args) 78 | { 79 | // EN: The client code. 80 | // 81 | // RU: Клиентский код. 82 | Singleton s1 = Singleton.GetInstance(); 83 | Singleton s2 = Singleton.GetInstance(); 84 | 85 | if (s1 == s2) 86 | { 87 | Console.WriteLine("Singleton works, both variables contain the same instance."); 88 | } 89 | else 90 | { 91 | Console.WriteLine("Singleton failed, variables contain different instances."); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Singleton.Conceptual/NonThreadSafe/Singleton.Conceptual.NonThreadSafe.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Singleton.Conceptual/ThreadSafe/Output.txt: -------------------------------------------------------------------------------- 1 | FOO 2 | FOO -------------------------------------------------------------------------------- /Singleton.Conceptual/ThreadSafe/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Singleton Design Pattern 2 | // 3 | // Intent: Lets you ensure that a class has only one instance, while providing a 4 | // global access point to this instance. 5 | // 6 | // RU: Паттерн Одиночка 7 | // 8 | // Назначение: Гарантирует, что у класса есть только один экземпляр, и 9 | // предоставляет к нему глобальную точку доступа. 10 | 11 | using System; 12 | using System.Threading; 13 | 14 | namespace Singleton 15 | { 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. 19 | // 20 | // RU: Эта реализация Одиночки называется "блокировка с двойной проверкой" 21 | // (double check lock). Она безопасна в многопоточной среде, а также 22 | // позволяет отложенную инициализацию объекта Одиночки. 23 | class Singleton 24 | { 25 | private Singleton() { } 26 | 27 | private static Singleton _instance; 28 | 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 | // время первого доступа к Одиночке. 34 | private static readonly object _lock = new object(); 35 | 36 | public static Singleton GetInstance(string value) 37 | { 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) 44 | { 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) 58 | { 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 | // RU: Первый поток достигает этого условия и проходит 68 | // внутрь, создавая объект-одиночку. Как только этот поток 69 | // покинет секцию и освободит блокировку, следующий поток 70 | // может снова установить блокировку и зайти внутрь. Однако 71 | // теперь экземпляр одиночки уже будет создан и поток не 72 | // сможет пройти через это условие, а значит новый объект не 73 | // будет создан. 74 | if (_instance == null) 75 | { 76 | _instance = new Singleton(); 77 | _instance.Value = value; 78 | } 79 | } 80 | } 81 | return _instance; 82 | } 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; } 89 | } 90 | 91 | class Program 92 | { 93 | static void Main(string[] args) 94 | { 95 | // EN: The client code. 96 | // 97 | // RU: Клиентский код. 98 | 99 | Console.WriteLine( 100 | "{0}\n{1}\n\n{2}\n", 101 | "If you see the same value, then singleton was reused (yay!)", 102 | "If you see different values, then 2 singletons were created (booo!!)", 103 | "RESULT:" 104 | ); 105 | 106 | Thread process1 = new Thread(() => 107 | { 108 | TestSingleton("FOO"); 109 | }); 110 | Thread process2 = new Thread(() => 111 | { 112 | TestSingleton("BAR"); 113 | }); 114 | 115 | process1.Start(); 116 | process2.Start(); 117 | 118 | process1.Join(); 119 | process2.Join(); 120 | } 121 | 122 | public static void TestSingleton(string value) 123 | { 124 | Singleton singleton = Singleton.GetInstance(value); 125 | Console.WriteLine(singleton.Value); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Singleton.Conceptual/ThreadSafe/Singleton.Conceptual.ThreadSafe.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /State.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Context: Transition to ConcreteStateA. 2 | ConcreteStateA handles request1. 3 | ConcreteStateA wants to change the state of the context. 4 | Context: Transition to ConcreteStateB. 5 | ConcreteStateB handles request2. 6 | ConcreteStateB wants to change the state of the context. 7 | Context: Transition to ConcreteStateA. -------------------------------------------------------------------------------- /State.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: State Design Pattern 2 | // 3 | // Intent: Lets an object alter its behavior when its internal state changes. It 4 | // appears as if the object changed its class. 5 | // 6 | // RU: Паттерн Состояние 7 | // 8 | // Назначение: Позволяет объектам менять поведение в зависимости от своего 9 | // состояния. Извне создаётся впечатление, что изменился класс объекта. 10 | 11 | using System; 12 | 13 | namespace RefactoringGuru.DesignPatterns.State.Conceptual 14 | { 15 | // EN: The Context defines the interface of interest to clients. It also 16 | // maintains a reference to an instance of a State subclass, which 17 | // represents the current state of the Context. 18 | // 19 | // RU: Контекст определяет интерфейс, представляющий интерес для клиентов. 20 | // Он также хранит ссылку на экземпляр подкласса Состояния, который 21 | // отображает текущее состояние Контекста. 22 | class Context 23 | { 24 | // EN: A reference to the current state of the Context. 25 | // 26 | // RU: Ссылка на текущее состояние Контекста. 27 | private State _state = null; 28 | 29 | public Context(State state) 30 | { 31 | this.TransitionTo(state); 32 | } 33 | 34 | // EN: The Context allows changing the State object at runtime. 35 | // 36 | // RU: Контекст позволяет изменять объект Состояния во время выполнения. 37 | public void TransitionTo(State state) 38 | { 39 | Console.WriteLine($"Context: Transition to {state.GetType().Name}."); 40 | this._state = state; 41 | this._state.SetContext(this); 42 | } 43 | 44 | // EN: The Context delegates part of its behavior to the current State 45 | // object. 46 | // 47 | // RU: Контекст делегирует часть своего поведения текущему объекту 48 | // Состояния. 49 | public void Request1() 50 | { 51 | this._state.Handle1(); 52 | } 53 | 54 | public void Request2() 55 | { 56 | this._state.Handle2(); 57 | } 58 | } 59 | 60 | // EN: The base State class declares methods that all Concrete State should 61 | // implement and also provides a backreference to the Context object, 62 | // associated with the State. This backreference can be used by States to 63 | // transition the Context to another State. 64 | // 65 | // RU: Базовый класс Состояния объявляет методы, которые должны реализовать 66 | // все Конкретные Состояния, а также предоставляет обратную ссылку на объект 67 | // Контекст, связанный с Состоянием. Эта обратная ссылка может 68 | // использоваться Состояниями для передачи Контекста другому Состоянию. 69 | abstract class State 70 | { 71 | protected Context _context; 72 | 73 | public void SetContext(Context context) 74 | { 75 | this._context = context; 76 | } 77 | 78 | public abstract void Handle1(); 79 | 80 | public abstract void Handle2(); 81 | } 82 | 83 | // EN: Concrete States implement various behaviors, associated with a state 84 | // of the Context. 85 | // 86 | // RU: Конкретные Состояния реализуют различные модели поведения, связанные 87 | // с состоянием Контекста. 88 | class ConcreteStateA : State 89 | { 90 | public override void Handle1() 91 | { 92 | Console.WriteLine("ConcreteStateA handles request1."); 93 | Console.WriteLine("ConcreteStateA wants to change the state of the context."); 94 | this._context.TransitionTo(new ConcreteStateB()); 95 | } 96 | 97 | public override void Handle2() 98 | { 99 | Console.WriteLine("ConcreteStateA handles request2."); 100 | } 101 | } 102 | 103 | class ConcreteStateB : State 104 | { 105 | public override void Handle1() 106 | { 107 | Console.Write("ConcreteStateB handles request1."); 108 | } 109 | 110 | public override void Handle2() 111 | { 112 | Console.WriteLine("ConcreteStateB handles request2."); 113 | Console.WriteLine("ConcreteStateB wants to change the state of the context."); 114 | this._context.TransitionTo(new ConcreteStateA()); 115 | } 116 | } 117 | 118 | class Program 119 | { 120 | static void Main(string[] args) 121 | { 122 | // EN: The client code. 123 | // 124 | // RU: Клиентский код. 125 | var context = new Context(new ConcreteStateA()); 126 | context.Request1(); 127 | context.Request2(); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /State.Conceptual/State.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Strategy.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Client: Strategy is set to normal sorting. 2 | Context: Sorting data using the strategy (not sure how it'll do it) 3 | a,b,c,d,e 4 | 5 | Client: Strategy is set to reverse sorting. 6 | Context: Sorting data using the strategy (not sure how it'll do it) 7 | e,d,c,b,a 8 | -------------------------------------------------------------------------------- /Strategy.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Strategy Design Pattern 2 | // 3 | // Intent: Lets you define a family of algorithms, put each of them into a 4 | // separate class, and make their objects interchangeable. 5 | // 6 | // RU: Паттерн Стратегия 7 | // 8 | // Назначение: Определяет семейство схожих алгоритмов и помещает каждый из них в 9 | // собственный класс, после чего алгоритмы можно взаимозаменять прямо во время 10 | // исполнения программы. 11 | 12 | using System; 13 | using System.Collections.Generic; 14 | 15 | namespace RefactoringGuru.DesignPatterns.Strategy.Conceptual 16 | { 17 | // EN: The Context defines the interface of interest to clients. 18 | // 19 | // RU: Контекст определяет интерфейс, представляющий интерес для клиентов. 20 | class Context 21 | { 22 | // EN: The Context maintains a reference to one of the Strategy objects. 23 | // The Context does not know the concrete class of a strategy. It should 24 | // work with all strategies via the Strategy interface. 25 | // 26 | // RU: Контекст хранит ссылку на один из объектов Стратегии. Контекст не 27 | // знает конкретного класса стратегии. Он должен работать со всеми 28 | // стратегиями через интерфейс Стратегии. 29 | private IStrategy _strategy; 30 | 31 | public Context() 32 | { } 33 | 34 | // EN: Usually, the Context accepts a strategy through the constructor, 35 | // but also provides a setter to change it at runtime. 36 | // 37 | // RU: Обычно Контекст принимает стратегию через конструктор, а также 38 | // предоставляет сеттер для её изменения во время выполнения. 39 | public Context(IStrategy strategy) 40 | { 41 | this._strategy = strategy; 42 | } 43 | 44 | // EN: Usually, the Context allows replacing a Strategy object at 45 | // runtime. 46 | // 47 | // RU: Обычно Контекст позволяет заменить объект Стратегии во время 48 | // выполнения. 49 | public void SetStrategy(IStrategy strategy) 50 | { 51 | this._strategy = strategy; 52 | } 53 | 54 | // EN: The Context delegates some work to the Strategy object instead of 55 | // implementing multiple versions of the algorithm on its own. 56 | // 57 | // RU: Вместо того, чтобы самостоятельно реализовывать множественные 58 | // версии алгоритма, Контекст делегирует некоторую работу объекту 59 | // Стратегии. 60 | public void DoSomeBusinessLogic() 61 | { 62 | Console.WriteLine("Context: Sorting data using the strategy (not sure how it'll do it)"); 63 | var result = this._strategy.DoAlgorithm(new List { "a", "b", "c", "d", "e" }); 64 | 65 | string resultStr = string.Empty; 66 | foreach (var element in result as List) 67 | { 68 | resultStr += element + ","; 69 | } 70 | 71 | Console.WriteLine(resultStr); 72 | } 73 | } 74 | 75 | // EN: The Strategy interface declares operations common to all supported 76 | // versions of some algorithm. 77 | // 78 | // The Context uses this interface to call the algorithm defined by Concrete 79 | // Strategies. 80 | // 81 | // RU: Интерфейс Стратегии объявляет операции, общие для всех поддерживаемых 82 | // версий некоторого алгоритма. 83 | // 84 | // Контекст использует этот интерфейс для вызова алгоритма, определённого 85 | // Конкретными Стратегиями. 86 | public interface IStrategy 87 | { 88 | object DoAlgorithm(object data); 89 | } 90 | 91 | // EN: Concrete Strategies implement the algorithm while following the base 92 | // Strategy interface. The interface makes them interchangeable in the 93 | // Context. 94 | // 95 | // RU: Конкретные Стратегии реализуют алгоритм, следуя базовому интерфейсу 96 | // Стратегии. Этот интерфейс делает их взаимозаменяемыми в Контексте. 97 | class ConcreteStrategyA : IStrategy 98 | { 99 | public object DoAlgorithm(object data) 100 | { 101 | var list = data as List; 102 | list.Sort(); 103 | 104 | return list; 105 | } 106 | } 107 | 108 | class ConcreteStrategyB : IStrategy 109 | { 110 | public object DoAlgorithm(object data) 111 | { 112 | var list = data as List; 113 | list.Sort(); 114 | list.Reverse(); 115 | 116 | return list; 117 | } 118 | } 119 | 120 | class Program 121 | { 122 | static void Main(string[] args) 123 | { 124 | // EN: The client code picks a concrete strategy and passes it to 125 | // the context. The client should be aware of the differences 126 | // between strategies in order to make the right choice. 127 | // 128 | // RU: Клиентский код выбирает конкретную стратегию и передаёт её в 129 | // контекст. Клиент должен знать о различиях между стратегиями, 130 | // чтобы сделать правильный выбор. 131 | var context = new Context(); 132 | 133 | Console.WriteLine("Client: Strategy is set to normal sorting."); 134 | context.SetStrategy(new ConcreteStrategyA()); 135 | context.DoSomeBusinessLogic(); 136 | 137 | Console.WriteLine(); 138 | 139 | Console.WriteLine("Client: Strategy is set to reverse sorting."); 140 | context.SetStrategy(new ConcreteStrategyB()); 141 | context.DoSomeBusinessLogic(); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Strategy.Conceptual/Strategy.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /TemplateMethod.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | Same client code can work with different subclasses: 2 | AbstractClass says: I am doing the bulk of the work 3 | ConcreteClass1 says: Implemented Operation1 4 | AbstractClass says: But I let subclasses override some operations 5 | ConcreteClass1 says: Implemented Operation2 6 | AbstractClass says: But I am doing the bulk of the work anyway 7 | 8 | Same client code can work with different subclasses: 9 | AbstractClass says: I am doing the bulk of the work 10 | ConcreteClass2 says: Implemented Operation1 11 | AbstractClass says: But I let subclasses override some operations 12 | ConcreteClass2 says: Overridden Hook1 13 | ConcreteClass2 says: Implemented Operation2 14 | AbstractClass says: But I am doing the bulk of the work anyway -------------------------------------------------------------------------------- /TemplateMethod.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Template Method Design Pattern 2 | // 3 | // Intent: Defines the skeleton of an algorithm in the superclass but lets 4 | // subclasses override specific steps of the algorithm without changing its 5 | // structure. 6 | // 7 | // RU: Паттерн Шаблонный метод 8 | // 9 | // Назначение: Определяет скелет алгоритма, перекладывая ответственность за 10 | // некоторые его шаги на подклассы. Паттерн позволяет подклассам переопределять 11 | // шаги алгоритма, не меняя его общей структуры. 12 | 13 | using System; 14 | 15 | namespace RefactoringGuru.DesignPatterns.TemplateMethod.Conceptual 16 | { 17 | // EN: The Abstract Class defines a template method that contains a skeleton 18 | // of some algorithm, composed of calls to (usually) abstract primitive 19 | // operations. 20 | // 21 | // Concrete subclasses should implement these operations, but leave the 22 | // template method itself intact. 23 | // 24 | // RU: Абстрактный Класс определяет шаблонный метод, содержащий скелет 25 | // некоторого алгоритма, состоящего из вызовов (обычно) абстрактных 26 | // примитивных операций. 27 | // 28 | // Конкретные подклассы должны реализовать эти операции, но оставить сам 29 | // шаблонный метод без изменений. 30 | abstract class AbstractClass 31 | { 32 | // EN: The template method defines the skeleton of an algorithm. 33 | // 34 | // RU: Шаблонный метод определяет скелет алгоритма. 35 | public void TemplateMethod() 36 | { 37 | this.BaseOperation1(); 38 | this.RequiredOperations1(); 39 | this.BaseOperation2(); 40 | this.Hook1(); 41 | this.RequiredOperation2(); 42 | this.BaseOperation3(); 43 | this.Hook2(); 44 | } 45 | 46 | // EN: These operations already have implementations. 47 | // 48 | // RU: Эти операции уже имеют реализации. 49 | protected void BaseOperation1() 50 | { 51 | Console.WriteLine("AbstractClass says: I am doing the bulk of the work"); 52 | } 53 | 54 | protected void BaseOperation2() 55 | { 56 | Console.WriteLine("AbstractClass says: But I let subclasses override some operations"); 57 | } 58 | 59 | protected void BaseOperation3() 60 | { 61 | Console.WriteLine("AbstractClass says: But I am doing the bulk of the work anyway"); 62 | } 63 | 64 | // EN: These operations have to be implemented in subclasses. 65 | // 66 | // RU: А эти операции должны быть реализованы в подклассах. 67 | protected abstract void RequiredOperations1(); 68 | 69 | protected abstract void RequiredOperation2(); 70 | 71 | // EN: These are "hooks." Subclasses may override them, but it's not 72 | // mandatory since the hooks already have default (but empty) 73 | // implementation. Hooks provide additional extension points in some 74 | // crucial places of the algorithm. 75 | // 76 | // RU: Это «хуки». Подклассы могут переопределять их, но это не 77 | // обязательно, поскольку у хуков уже есть стандартная (но пустая) 78 | // реализация. Хуки предоставляют дополнительные точки расширения в 79 | // некоторых критических местах алгоритма. 80 | protected virtual void Hook1() { } 81 | 82 | protected virtual void Hook2() { } 83 | } 84 | 85 | // EN: Concrete classes have to implement all abstract operations of the 86 | // base class. They can also override some operations with a default 87 | // implementation. 88 | // 89 | // RU: Конкретные классы должны реализовать все абстрактные операции 90 | // базового класса. Они также могут переопределить некоторые операции с 91 | // реализацией по умолчанию. 92 | class ConcreteClass1 : AbstractClass 93 | { 94 | protected override void RequiredOperations1() 95 | { 96 | Console.WriteLine("ConcreteClass1 says: Implemented Operation1"); 97 | } 98 | 99 | protected override void RequiredOperation2() 100 | { 101 | Console.WriteLine("ConcreteClass1 says: Implemented Operation2"); 102 | } 103 | } 104 | 105 | // EN: Usually, concrete classes override only a fraction of base class' 106 | // operations. 107 | // 108 | // RU: Обычно конкретные классы переопределяют только часть операций 109 | // базового класса. 110 | class ConcreteClass2 : AbstractClass 111 | { 112 | protected override void RequiredOperations1() 113 | { 114 | Console.WriteLine("ConcreteClass2 says: Implemented Operation1"); 115 | } 116 | 117 | protected override void RequiredOperation2() 118 | { 119 | Console.WriteLine("ConcreteClass2 says: Implemented Operation2"); 120 | } 121 | 122 | protected override void Hook1() 123 | { 124 | Console.WriteLine("ConcreteClass2 says: Overridden Hook1"); 125 | } 126 | } 127 | 128 | class Client 129 | { 130 | // EN: The client code calls the template method to execute the 131 | // algorithm. Client code does not have to know the concrete class of an 132 | // object it works with, as long as it works with objects through the 133 | // interface of their base class. 134 | // 135 | // RU: Клиентский код вызывает шаблонный метод для выполнения алгоритма. 136 | // Клиентский код не должен знать конкретный класс объекта, с которым 137 | // работает, при условии, что он работает с объектами через интерфейс их 138 | // базового класса. 139 | public static void ClientCode(AbstractClass abstractClass) 140 | { 141 | // ... 142 | abstractClass.TemplateMethod(); 143 | // ... 144 | } 145 | } 146 | 147 | class Program 148 | { 149 | static void Main(string[] args) 150 | { 151 | Console.WriteLine("Same client code can work with different subclasses:"); 152 | 153 | Client.ClientCode(new ConcreteClass1()); 154 | 155 | Console.Write("\n"); 156 | 157 | Console.WriteLine("Same client code can work with different subclasses:"); 158 | Client.ClientCode(new ConcreteClass2()); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /TemplateMethod.Conceptual/TemplateMethod.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | -------------------------------------------------------------------------------- /Visitor.Conceptual/Output.txt: -------------------------------------------------------------------------------- 1 | The client code works with all visitors via the base Visitor interface: 2 | A + ConcreteVisitor1 3 | B + ConcreteVisitor1 4 | 5 | It allows the same client code to work with different types of visitors: 6 | A + ConcreteVisitor2 7 | B + ConcreteVisitor2 -------------------------------------------------------------------------------- /Visitor.Conceptual/Program.cs: -------------------------------------------------------------------------------- 1 | // EN: Visitor Design Pattern 2 | // 3 | // Intent: Lets you separate algorithms from the objects on which they operate. 4 | // 5 | // RU: Паттерн Посетитель 6 | // 7 | // Назначение: Позволяет создавать новые операции, не меняя классы объектов, над 8 | // которыми эти операции могут выполняться. 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | 13 | namespace RefactoringGuru.DesignPatterns.Visitor.Conceptual 14 | { 15 | // EN: The Component interface declares an `accept` method that should take 16 | // the base visitor interface as an argument. 17 | // 18 | // RU: Интерфейс Компонента объявляет метод accept, который в качестве 19 | // аргумента может получать любой объект, реализующий интерфейс посетителя. 20 | public interface IComponent 21 | { 22 | void Accept(IVisitor visitor); 23 | } 24 | 25 | // EN: Each Concrete Component must implement the `Accept` method in such a 26 | // way that it calls the visitor's method corresponding to the component's 27 | // class. 28 | // 29 | // RU: Каждый Конкретный Компонент должен реализовать метод Accept таким 30 | // образом, чтобы он вызывал метод посетителя, соответствующий классу 31 | // компонента. 32 | public class ConcreteComponentA : IComponent 33 | { 34 | // EN: Note that we're calling `VisitConcreteComponentA`, which matches 35 | // the current class name. This way we let the visitor know the class of 36 | // the component it works with. 37 | // 38 | // RU: Обратите внимание, мы вызываем VisitConcreteComponentA, что 39 | // соответствует названию текущего класса. Таким образом мы позволяем 40 | // посетителю узнать, с каким классом компонента он работает. 41 | public void Accept(IVisitor visitor) 42 | { 43 | visitor.VisitConcreteComponentA(this); 44 | } 45 | 46 | // EN: Concrete Components may have special methods that don't exist in 47 | // their base class or interface. The Visitor is still able to use these 48 | // methods since it's aware of the component's concrete class. 49 | // 50 | // RU: Конкретные Компоненты могут иметь особые методы, не объявленные в 51 | // их базовом классе или интерфейсе. Посетитель всё же может 52 | // использовать эти методы, поскольку он знает о конкретном классе 53 | // компонента. 54 | public string ExclusiveMethodOfConcreteComponentA() 55 | { 56 | return "A"; 57 | } 58 | } 59 | 60 | public class ConcreteComponentB : IComponent 61 | { 62 | // EN: Same here: VisitConcreteComponentB => ConcreteComponentB 63 | // 64 | // RU: То же самое здесь: VisitConcreteComponentB => ConcreteComponentB 65 | public void Accept(IVisitor visitor) 66 | { 67 | visitor.VisitConcreteComponentB(this); 68 | } 69 | 70 | public string SpecialMethodOfConcreteComponentB() 71 | { 72 | return "B"; 73 | } 74 | } 75 | 76 | // EN: The Visitor Interface declares a set of visiting methods that 77 | // correspond to component classes. The signature of a visiting method 78 | // allows the visitor to identify the exact class of the component that it's 79 | // dealing with. 80 | // 81 | // RU: Интерфейс Посетителя объявляет набор методов посещения, 82 | // соответствующих классам компонентов. Сигнатура метода посещения позволяет 83 | // посетителю определить конкретный класс компонента, с которым он имеет 84 | // дело. 85 | public interface IVisitor 86 | { 87 | void VisitConcreteComponentA(ConcreteComponentA element); 88 | 89 | void VisitConcreteComponentB(ConcreteComponentB element); 90 | } 91 | 92 | // EN: Concrete Visitors implement several versions of the same algorithm, 93 | // which can work with all concrete component classes. 94 | // 95 | // You can experience the biggest benefit of the Visitor pattern when using 96 | // it with a complex object structure, such as a Composite tree. In this 97 | // case, it might be helpful to store some intermediate state of the 98 | // algorithm while executing visitor's methods over various objects of the 99 | // structure. 100 | // 101 | // RU: Конкретные Посетители реализуют несколько версий одного и того же 102 | // алгоритма, которые могут работать со всеми классами конкретных 103 | // компонентов. 104 | // 105 | // Максимальную выгоду от паттерна Посетитель вы почувствуете, используя его 106 | // со сложной структурой объектов, такой как дерево Компоновщика. В этом 107 | // случае было бы полезно хранить некоторое промежуточное состояние 108 | // алгоритма при выполнении методов посетителя над различными объектами 109 | // структуры. 110 | class ConcreteVisitor1 : IVisitor 111 | { 112 | public void VisitConcreteComponentA(ConcreteComponentA element) 113 | { 114 | Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor1"); 115 | } 116 | 117 | public void VisitConcreteComponentB(ConcreteComponentB element) 118 | { 119 | Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor1"); 120 | } 121 | } 122 | 123 | class ConcreteVisitor2 : IVisitor 124 | { 125 | public void VisitConcreteComponentA(ConcreteComponentA element) 126 | { 127 | Console.WriteLine(element.ExclusiveMethodOfConcreteComponentA() + " + ConcreteVisitor2"); 128 | } 129 | 130 | public void VisitConcreteComponentB(ConcreteComponentB element) 131 | { 132 | Console.WriteLine(element.SpecialMethodOfConcreteComponentB() + " + ConcreteVisitor2"); 133 | } 134 | } 135 | 136 | public class Client 137 | { 138 | // EN: The client code can run visitor operations over any set of 139 | // elements without figuring out their concrete classes. The accept 140 | // operation directs a call to the appropriate operation in the visitor 141 | // object. 142 | // 143 | // RU: Клиентский код может выполнять операции посетителя над любым 144 | // набором элементов, не выясняя их конкретных классов. Операция 145 | // принятия направляет вызов к соответствующей операции в объекте 146 | // посетителя. 147 | public static void ClientCode(List components, IVisitor visitor) 148 | { 149 | foreach (var component in components) 150 | { 151 | component.Accept(visitor); 152 | } 153 | } 154 | } 155 | 156 | class Program 157 | { 158 | static void Main(string[] args) 159 | { 160 | List components = new List 161 | { 162 | new ConcreteComponentA(), 163 | new ConcreteComponentB() 164 | }; 165 | 166 | Console.WriteLine("The client code works with all visitors via the base Visitor interface:"); 167 | var visitor1 = new ConcreteVisitor1(); 168 | Client.ClientCode(components,visitor1); 169 | 170 | Console.WriteLine(); 171 | 172 | Console.WriteLine("It allows the same client code to work with different types of visitors:"); 173 | var visitor2 = new ConcreteVisitor2(); 174 | Client.ClientCode(components, visitor2); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /Visitor.Conceptual/Visitor.Conceptual.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net8.0 5 | 6 | --------------------------------------------------------------------------------