Design pattern cheatsheet

Creational patterns

Singleton pattern

sealed class Singleton
{
	public static Singleton Instance { get; } = new Singleton();

	Singleton() { }

	public void Operation() => Console.WriteLine(GetHashCode());
}

public class Client
{
	public void Main()
	{
		Singleton s1 = Singleton.Instance;
		Singleton s2 = Singleton.Instance;

		s1.Operation(); // 33163964
		s2.Operation(); // 33163964
	}
}

Prototype pattern

interface IPrototype<out T> : ICloneable
{
	new T Clone();
}

class ConcretePrototypeA : IPrototype<ConcretePrototypeA>
{
	public string Data { get; set; }
	public ConcretePrototypeA Clone() => (ConcretePrototypeA) MemberwiseClone();
	object ICloneable.Clone() => Clone();
}

class ConcretePrototypeB : IPrototype<ConcretePrototypeB>
{
	public string Data { get; set; }
	public ConcretePrototypeA Reference { get; set; }
	public IList<ConcretePrototypeA> Iterable { get; set; }
	
	public ConcretePrototypeB Clone()
	{
		var clone = (ConcretePrototypeB)MemberwiseClone();
		clone.Reference = Reference.Clone();
		clone.Iterable = new List<ConcretePrototypeA>(Iterable);
		for (int i = 0; i < Iterable.Count; i++)
		{
			clone.Iterable[i] = Iterable[i].Clone();
		}

		return clone;
	}

	object ICloneable.Clone() => Clone();
}

public class Client
{
	public void Main()
	{
		ConcretePrototypeA pa = new ConcretePrototypeA { Data = "A" };
		ConcretePrototypeA pa1 = new ConcretePrototypeA { Data = "A1" };
		ConcretePrototypeA pa2 = new ConcretePrototypeA { Data = "A2" };
		ConcretePrototypeB pb = new ConcretePrototypeB
		{
			Data = "B",
			Reference = pa,
			Iterable = new List<ConcretePrototypeA> {pa1, pa2}
		};

		ConcretePrototypeA ca = pa.Clone();
		ConcretePrototypeB cb = pb.Clone();

		ca.Data = "CA";
		cb.Reference.Data = "CB";
		cb.Iterable[0].Data = "CA1";

		Console.WriteLine($"pa := {pa.Data}; ca := {ca.Data}");
		Console.WriteLine($"pb := {pb.Reference.Data}; cb := {cb.Reference.Data}");
		Console.WriteLine($"pb := {pb.Iterable[0].Data}; cb := {cb.Iterable[0].Data}");

		/**
		 * OUTPUT:
		 * pa := A; ca := CA
		 * pb := A; cb := CB
		 * pb := A1; cb := CA1
		 */
	}
}

Factory pattern

interface IProduct
{
	void Operation();
}

class ConcreteProductA : IProduct
{
	public void Operation() => Console.WriteLine("Product A");
}

class ConcreteProductB : IProduct
{
	public void Operation() => Console.WriteLine("Product B");
}

class Factory
{
	public IProduct Create(string method) => 
		method == "A" ? (IProduct) new ConcreteProductA() : new ConcreteProductB();
}

public class Client
{
	public void Main()
	{
		Factory factory = new Factory();
		IProduct a = factory.Create("A");
		IProduct b = factory.Create("B");

		a.Operation(); // Product A
		b.Operation(); // Product B
	}
}

Abstract factory pattern

interface IProductA
{
	void Operation();
}

interface IProductB
{
	void Operation();
}

interface IFactory
{
	IProductA CreateA();
	IProductB CreateB();
}

class ConcreteProductA1 : IProductA
{
	public void Operation() => Console.Write("Product A1");
}

class ConcreteProductA2 : IProductA
{
	public void Operation() => Console.Write("Product A2");
}

class ConcreteProductB1 : IProductB
{
	public void Operation() => Console.Write("Product B1");
}

class ConcreteProductB2 : IProductB
{
	public void Operation() => Console.Write("Product B2");
}

class ConcreteFactory1 : IFactory
{
	public IProductA CreateA() => new ConcreteProductA1();
	public IProductB CreateB() => new ConcreteProductB1();
}

class ConcreteFactory2 : IFactory
{
	public IProductA CreateA() => new ConcreteProductA2();
	public IProductB CreateB() => new ConcreteProductB2();
}

class Context
{
	private IProductA _a;
	private IProductB _b;

	public Context(IFactory factory)
	{
		_a = factory.CreateA();
		_b = factory.CreateB();
	}

	public void Operation()
	{
		_a.Operation();
		Console.Write(" and ");
		_b.Operation();
		Console.WriteLine();
	}
}

public class Client
{
	public void Main()
	{
		IFactory f1 = new ConcreteFactory1();
		IFactory f2 = new ConcreteFactory2();

		Context context = new Context(f1);
		context.Operation(); // Product A1 and Product B1

		context = new Context(f2);
		context.Operation(); // Product A2 and Product B2
	}
}

Builder pattern

interface IBuilder
{
	void BuildPartA();
	void BuildPartB();
	Product GetResult();
}

class ConcreteBuilderA : IBuilder
{
	private Product _product = new Product();
	public void BuildPartA() => _product.Add("Part(A)");
	public void BuildPartB() => _product.Add("Part(B)");
	public Product GetResult() => _product;
}

class ConcreteBuilderB : IBuilder
{
	private Product _product = new Product();
	public void BuildPartA() => _product.Add("Part(X)");
	public void BuildPartB() => _product.Add("Part(Y)");
	public Product GetResult() => _product;
}

class Director
{
	public void Construct(IBuilder builder)
	{
		builder.BuildPartA();
		builder.BuildPartB();
	}
}

class Product
{
	private ISet<string> _parts = new HashSet<string>();

	public void Add(string part) => _parts.Add(part);

	public void Display()
	{
		foreach (string part in _parts)
		{
			Console.Write(part + " ");
		}
	}
}

public class Client
{
	public void Main()
	{
		var director = new Director();

		IBuilder b1 = new ConcreteBuilderA();
		IBuilder b2 = new ConcreteBuilderB();

		director.Construct(b1);
		Product p1 = b1.GetResult();
		p1.Display(); // Part(A) Part(B) 

		director.Construct(b2);
		Product p2 = b2.GetResult();
		p2.Display(); // Part(X) Part(Y) 
	}
}

Structural patterns

Decorator pattern

interface IComponent
{
	void Operation();
}

class ConcreteComponent : IComponent
{
	public void Operation() => Console.WriteLine("ConcreteComponent");
}

class DecoratedComponentA : IComponent
{
	private IComponent _component;

	public DecoratedComponentA(IComponent component) => _component = component;

	public void Operation()
	{
		_component.Operation();
		Console.WriteLine("DecoratedComponentA");
	}
}

class DecoratedComponentB : IComponent
{
	private IComponent _component;

	public DecoratedComponentB(IComponent component) => _component = component;

	public void Operation()
	{
		_component.Operation();
		Console.WriteLine("DecoratedComponentB");
	}
}

public class Client
{
	public void Main()
	{
		IComponent component = new ConcreteComponent();
		component = new DecoratedComponentA(component);
		component = new DecoratedComponentB(component);

		component.Operation();

		/**
		 * OUTPUT:
		 *
		 * ConcreteComponent
		 * DecoratedComponentA
		 * DecoratedComponentB
		 */
	}
}

Proxy pattern

interface ISubject
{
	void Request();
}

class ConcreteSubject : ISubject
{
	public void Request() => Console.WriteLine("ConcreteSubject");
}

class SubjectProxy : ISubject
{
	private ISubject _subject;

	public void Request()
	{
		if (_subject == null)
		{
			Console.WriteLine("Proxy inactive");
			_subject = new ConcreteSubject();
		}

		Console.Write("Proxy call to: ");
		_subject.Request();
	}
}

public class Client
{
	public void Main()
	{
		ISubject subject = new ConcreteSubject();
		subject.Request();
		/**
		 * OUTPUT:
		 * ConcreteSubject
		 */

		subject = new SubjectProxy();
		subject.Request();
		/**
		 * OUTPUT:
		 * Proxy inactive
		 * Proxy call to: ConcreteSubject
		 */
	}
}

Bridge pattern

interface IBridge
{
	void Operation();
}

class ConcreteImplementationA : IBridge
{
	public void Operation() => Console.WriteLine("Implementation A");
}

class ConcreteImplementationB : IBridge
{
	public void Operation() => Console.WriteLine("Implementation B");
}

class Context
{
	private IBridge _impl;

	public Context(IBridge impl) => _impl = impl;
	public void Operation() => _impl.Operation();
}

public class Client
{
	public void Main()
	{
		Context a = new Context(new ConcreteImplementationA());
		Context b = new Context(new ConcreteImplementationB());

		a.Operation(); // Implementation A
		b.Operation(); // Implementation B
	}
}

Composite pattern

interface IComponent
{
	void Operation();
}

class ConcreteComponentA : IComponent
{
	public void Operation() => Console.WriteLine("Component A");
}

class ConcreteComponentB : IComponent
{
	public void Operation() => Console.WriteLine("Component B");
}

class ComposedComponent : IComponent
{
	private IList<IComponent> _components;

	public ComposedComponent(IList<IComponent> components)
	{
		_components = components;
	}

	public void Operation()
	{
		foreach (IComponent component in _components)
		{
			component.Operation();
		}
	}
}

public class Client
{
	public void Main()
	{
		IComponent component = new ComposedComponent(new List<IComponent>
		{
			new ConcreteComponentA(),
			new ConcreteComponentB()
		});

		component.Operation();
		/**
		 * OUTPUT:
		 * Component A
		 * Component B
		 */
	}
}

Flyweight pattern

interface IFlyweight
{
	void Operation();
}

class ConcreteFlyweightA : IFlyweight
{
	public void Operation() => Console.WriteLine("Flyweight A");
}

class ConcreteFlyweightB : IFlyweight
{
	public void Operation() => Console.WriteLine("Flyweight B");
}

class ConcreteFlyweightC : IFlyweight
{
	public void Operation() => Console.WriteLine("Flyweight C");
}

class ConcreteFlyweight : IFlyweight
{
	public void Operation() => Console.WriteLine("Flyweight<T>");
}

class FlyweightFactory
{
	private IDictionary<string, IFlyweight> _flyweights 
		= new Dictionary<string, IFlyweight>();

	public IFlyweight this[string idx]
	{
		get
		{
			if (!_flyweights.ContainsKey(idx))
			{
				_flyweights.Add(idx, Create(idx));
			}

			return _flyweights[idx];
		}
	}

	private IFlyweight Create(string idx)
	{
		switch (idx)
		{
			case "A": return new ConcreteFlyweightA();
			case "B": return new ConcreteFlyweightB();
			case "C": return new ConcreteFlyweightC();
			default: return new ConcreteFlyweight();
		}
	}
}

public class Client
{
	public void Main()
	{
		FlyweightFactory flyweights = new FlyweightFactory();
		string[] range = new[] { "A", "B", "C", "D", "E" };

		foreach (string idx in range)
		{
			flyweights[idx].Operation();
		}

		/**
		 * OUTPUT:
		 * Flyweight A
		 * Flyweight B
		 * Flyweight C
		 * Flyweight<T>
		 * Flyweight<T>
		 */
	}
}

Adapter pattern

interface ITarget
{
	void Request();
}

class LegacyCode
{
	public void Important() => Console.WriteLine("important legacy code");
}

class Adaptee
{
	public void SpecificRequest() => Console.WriteLine("request");
}

class LegacyAdapter : LegacyCode, ITarget
{
	public void Request() => Important();
}

class Adapter : Adaptee, ITarget
{
	public void Request()
	{
		Console.Write("modified ");
		SpecificRequest();
	}
}

public class Client
{
	public void Main()
	{
		ITarget a = new LegacyAdapter();
		ITarget b = new Adapter();

		a.Request(); // important legacy code
		b.Request(); // modified request

	}
}

Facade pattern

internal interface ISubSystem
{
	void Operation();
	string WhoAmI();
}

internal class SubSystemA:ISubSystem
{
	public void Operation() => Console.WriteLine("OpA");
	public string WhoAmI() => "A";
}

internal class SubSystemB : ISubSystem
{
	public void Operation() => Console.WriteLine("OpB");
	public string WhoAmI() => "B";
}

internal class SubSystemC : ISubSystem
{
	public void Operation() => Console.WriteLine("OpC");
	public string WhoAmI() => "C";
}

public static class Facade
{
	private static ISubSystem _a = new SubSystemA();
	private static ISubSystem _b = new SubSystemB();
	private static ISubSystem _c = new SubSystemC();

	public static void OperationA() => _a.Operation();
	public static void OperationB() => _b.Operation();
	public static void OperationC() => _c.Operation();

	public static void Simplify() 
		=> Console.WriteLine(_a.WhoAmI() + _b.WhoAmI() + _c.WhoAmI());
}

public class Client
{
	public void Main()
	{
		Facade.OperationA(); // OpA
		Facade.OperationB(); // OpB
		Facade.OperationC(); // OpC
		Facade.Simplify();  // ABC
	}
}

Behavioral patterns

Strategy pattern

interface IStrategy
{
	void Algorithm();
}

class ConcreteStrategyA : IStrategy
{
	public void Algorithm() => Console.WriteLine("Strategy A");
}

class ConcreteStrategyB : IStrategy
{
	public void Algorithm() => Console.WriteLine("Strategy B");
}

class ConcreteStrategyC : IStrategy
{
	public void Algorithm() => Console.WriteLine("Strategy C");
}

class Context
{
	private IStrategy _strategy;
	
	public Context(IStrategy strategy) => SetStrategy(strategy);
	public void SetStrategy(IStrategy strategy) => _strategy = strategy;
	public void Algorithm() => _strategy.Algorithm();
}

public class Client
{
    public void Main()
    {
        Context context = new Context(new ConcreteStrategyA());
        context.Algorithm(); // Strategy A
        
        context.SetStrategy(new ConcreteStrategyB());
        context.Algorithm(); // Strategy B

        context.SetStrategy(new ConcreteStrategyC());
        context.Algorithm(); // Strategy C
    }
}

State pattern

interface IState
{
	void Handle(IContext context);
}

interface IContext
{
	void SetState(IState state);
	void Request();
}

class ConcreteStateA : IState
{
	public void Handle(IContext context)
	{
		Console.WriteLine("State A");
		context.SetState(new ConcreteStateB());
	}
}

class ConcreteStateB : IState
{
	public void Handle(IContext context)
	{
		Console.WriteLine("State B");
		context.SetState(new ConcreteStateA());
	}
}

class Context : IContext
{
	private IState _state = new ConcreteStateA();
	public void SetState(IState state) => _state = state;
	public void Request() => _state?.Handle(this);
}

public class Client
{
	public void Main()
	{
		Context context = new Context();
		context.Request();  // State A
		context.Request();  // State B
		context.Request();  // State A
	}
}

Template method pattern

interface IPrimitive
{
	void Operation();
}

class AlgorithmA : IPrimitive
{
	public void Operation() => Console.WriteLine("Algorithm A");
}

class AlgorithmB : IPrimitive
{
	public void Operation() => Console.WriteLine("Algorithm B");
}

class AlgorithmC : IPrimitive
{
	public void Operation() => Console.WriteLine("Algorithm C");
}

class Context
{
	public void Operation(IPrimitive a) => a?.Operation();
}

public class Client
{
	public void Main()
	{
		Context context = new Context();
		context.Operation(new AlgorithmA()); // Algorithm A
		context.Operation(new AlgorithmB()); // Algorithm B
		context.Operation(new AlgorithmC()); // Algorithm C
	}
}

Chain of responsibility pattern

interface IHandler
{
	IHandler Successor { get; set; }
	void Request();
}

class ConcreteHandlerA : IHandler
{
	public IHandler Successor { get; set; } = new ConcreteHandlerB();
	public void Request()
	{
		Console.WriteLine("Handler A");
		Successor?.Request();
	}
}

class ConcreteHandlerB : IHandler
{
	public IHandler Successor { get; set; } = new ConcreteHandlerC();
	public void Request()
	{
		Console.WriteLine("Handler B");
		Successor?.Request();
	}
}

class ConcreteHandlerC : IHandler
{
	public IHandler Successor { get; set; } 
	public void Request()
	{
		Console.WriteLine("Handler C");
		Successor?.Request();
	}
}

public class Client
{
	public void Main()
	{
		IHandler handler = new ConcreteHandlerA();

		handler.Request();
		// OUTPUT:
		// Handler A
		// Handler B
		// Handler C
	}
}

Command pattern

interface ICommand
{
	void Execute();
}

class ConcreteCommandA : ICommand
{
	private IReceiver _receiver;
	
	public ConcreteCommandA(IReceiver receiver) => _receiver = receiver;
	public void Execute() => _receiver?.Action();
}

class ConcreteCommandB : ICommand
{
	private IReceiver _receiver;
	
	public ConcreteCommandB(IReceiver receiver) => _receiver = receiver;
	public void Execute() => _receiver?.Action();
}

class Invoker
{
	private ICommand _commandA;
	private ICommand _commandB;

	public Invoker(ICommand commandA, ICommand commandB)
	{
		_commandA = commandA;
		_commandB = commandB;
	}

	public void ExecuteA() => _commandA?.Execute();
	public void ExecuteB() => _commandB?.Execute();
}

interface IReceiver
{
	void Action();
}

class ConcreteReceiverA : IReceiver
{
	public void Action() => Console.WriteLine("Action A");
}
class ConcreteReceiverB : IReceiver
{
	public void Action() => Console.WriteLine("Action B");
}

public class Client1
{
	public void Main()
	{
		ICommand commandA = new ConcreteCommandA(new ConcreteReceiverA());
		ICommand commandB = new ConcreteCommandB(new ConcreteReceiverB());
		Invoker invoker = new Invoker(commandA, commandB);

		invoker.ExecuteA(); // Action A
		invoker.ExecuteB(); // Action B
	}
}

Iterator pattern

interface IIterator
{
	bool IsDone();
	int GetItem();
}

interface IAggregate
{
	IIterator GetIterator();
}

class ConcreteIterator : IIterator
{
	private int _index;
	public bool IsDone() => _index >= 10;
	public int GetItem() => _index++;
}

class ConcreteAggregate : IAggregate
{
	public IIterator GetIterator() => new ConcreteIterator();
}

// C# only
class Collection : IEnumerable<int>
{
	public IEnumerator<int> GetEnumerator()
	{
		for (int i = 0; i < 10; i++)
		{
			yield return i;
		}
	}

	IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public class Client
{
	public void Main()
	{
		IAggregate aggregate = new ConcreteAggregate();
		IIterator iterator = aggregate.GetIterator();
		while (!iterator.IsDone())
		{
			Console.Write(iterator.GetItem() + " ");
		}
		// OUTPUT:
		// 0 1 2 3 4 5 6 7 8 9
	}

	public void CSharpOnly()
	{
		Collection collection = new Collection();
		foreach (int i in collection)
		{
			Console.Write(i + " ");
		}
		// OUTPUT:
		// 0 1 2 3 4 5 6 7 8 9
	}
}

Mediator pattern

interface IMediator
{
	void Register(IParticipant participant);
	void Broadcast(string message, IParticipant sender);
	void Notify(string message, IParticipant receiver);
}

interface IParticipant
{
	void Send(string message);
	void Send(string message, IParticipant receiver);
	void Receive(string message);
}

class ConcreteMediator : IMediator
{
	private ISet<IParticipant> _participants = new HashSet<IParticipant>();

	public void Register(IParticipant participant) => _participants.Add(participant);

	public void Broadcast(string message, IParticipant sender)
	{
		foreach (IParticipant participant in _participants)
		{
			Notify(message, participant);	
		}
	}

	public void Notify(string message, IParticipant receiver)
	{
		receiver.Receive(message);
	}
}

class ConcreteParticipant : IParticipant
{
	private IMediator _mediator;
	private string _name;

	public ConcreteParticipant(IMediator mediator)
	{
		_name = name;
		_mediator = mediator;
		_mediator.Register(this);
	}

	public void Send(string message) 
		=> _mediator.Broadcast($"{_name}: {message}", this);
	
	public void Send(string message, IParticipant receiver) 
		=> _mediator.Notify($"{_name}: {message}", receiver);
		
	public void Receive(string message) 
		=> Console.WriteLine($"[{_name}] {message}");
}

public class Client
{
	public void Main()
	{
		IMediator mediator = new ConcreteMediator();
		IParticipant alice = new ConcreteParticipant(mediator, "Alice");
		IParticipant bob = new ConcreteParticipant(mediator, "Bob");
		IParticipant carol = new ConcreteParticipant(mediator, "Carol");

		alice.Send("Hi all");
		bob.Send("Hello Alice", alice);
		carol.Send("Hello Alice", alice);

		/**
		 * OUTPUT:
		 *
		 * [Alice] Alice: Hi all
		 * [Bob] Alice: Hi all
		 * [Carol] Alice: Hi all
		 * [Alice] Bob: Hello Alice
		 * [Alice] Carol: Hello Alice
		 **/
	}
}

Observer pattern

interface IObserver
{
	void Update();
}

class ConcreteObserver : IObserver
{
	private ISubject _subject;
	private object _state;
	private string _name;
	
	public ConcreteObserver(ISubject subject, string name)
	{
		_subject = subject;
		_name = name;
	}

	public void Update()
	{
		_state = _subject.State;
		Console.WriteLine($"{_name} receive {_state}");
	}
}

interface ISubject
{
	object State { get; set; }
	void Attach(IObserver observer);
	void Detach(IObserver observer);
	void Notify(string message);
}

class ConcreteSubject : ISubject
{
	private ISet<IObserver> _observers = new HashSet<IObserver>();

	public object State { get; set; }
	public void Attach(IObserver observer) => _observers.Add(observer);
	public void Detach(IObserver observer) => _observers.Remove(observer);

	public void Notify(string message)
	{
		State = message;

		foreach (IObserver observer in _observers)
		{
			observer.Update();
		}
	}
}

public class Client
{
	public void Main()
	{
		ISubject subject = new ConcreteSubject();
		IObserver alice = new ConcreteObserver(subject, "Alice");
		IObserver bob = new ConcreteObserver(subject, "Bob");
		IObserver carol = new ConcreteObserver(subject, "Carol");
		
		subject.Attach(alice);
		subject.Attach(bob);
		subject.Attach(carol);
		subject.Notify("1st message");

		subject.Detach(carol);
		subject.Notify("2nd message");

		/**
		 * OUTPUT:
		 *
		 * Alice receive 1st message
		 * Bob receive 1st message
		 * Carol receive 1st message
		 * Alice receive 2nd message
		 * Bob receive 2nd message
		 **/
	}
}

Visitor pattern

interface IVisitor
{
	void Visit(IElement element);
}

class ConcreteVisitorA : IVisitor
{
	public void Visit(IElement element) => Console.WriteLine("Visitor A");
}

class ConcreteVisitorB : IVisitor
{
	public void Visit(IElement element) => Console.WriteLine("Visitor B");
}

interface IElement
{
	void Receive(IVisitor visitor);
}

class ConcreteElementA : IElement
{
	public void Receive(IVisitor visitor)
	{
		Console.Write("Element A receives ");
		visitor.Visit(this);
	}
}

class ConcreteElementB : IElement
{
	public void Receive(IVisitor visitor)
	{
		Console.Write("Element B receives ");
		visitor.Visit(this);
	}
}

class ComposedElement : IElement
{
	private IEnumerable<IElement> _elements;

	public ComposedElement(IEnumerable<IElement> elements)
	{
		_elements = elements;
	}

	public void Receive(IVisitor visitor)
	{
		foreach (IElement element in _elements)
		{
			element.Receive(visitor);
		}
	}
}

public class Client
{
	public void Main()
	{
		IElement element = new ComposedElement(new IElement[]
		{
			new ConcreteElementA(),
			new ConcreteElementB()
		});

		element.Receive(new ConcreteVisitorA());
		element.Receive(new ConcreteVisitorB());

		/**
		 * OUTPUT:
		 * Element A receives Visitor A
		 * Element B receives Visitor A
		 * Element A receives Visitor B
		 * Element B receives Visitor B
		 */
	}
}

Interpreter pattern

interface ITerm
{
	void Interpret(IContext context);
}

interface IContext
{
	IContext Sub { get; set; }
	string Name { get; set; }
}

class TerminalExpr : ITerm
{
	public void Interpret(IContext context)
	{
		Console.WriteLine($"Terminal.Interpret({context.Name})");
	}
}

class NonterminalExpr : ITerm
{
	public ITerm TerminalExpression { get; set; }

	public void Interpret(IContext context)
	{
		if (context.Sub != null)
		{
			Console.WriteLine($"Nonterminal.Interpret({context.Name})");
			Interpret(context.Sub);
		}
		else
		{
			TerminalExpression.Interpret(context);
		}
	}
}

class ConcreteContext : IContext
{
	public IContext Sub { get; set; }
	public string Name { get; set; }
}

public class Client
{
	public void Main()
	{
		IContext context = new ConcreteContext
		{
			Name = "Tag",
			Sub = new ConcreteContext
			{
				Name = "SubTag",
				Sub = new ConcreteContext()
				{
					Name = "Attr"
				}
			}
		};

		ITerm term = new NonterminalExpr
		{
			TerminalExpression = new TerminalExpr()
		};
		term.Interpret(context);

		/**
		 * OUTPUT:
		 *
		 * Nonterminal.Interpret(Tag)
		 * Nonterminal.Interpret(SubTag)
		 * Terminal.Interpret(Attr)
		 */
	}
}

Memento pattern

interface IMemento
{
	void Save(object obj);
	object Restore();
}

interface IOriginator
{
	string State { get; set; }
	void Operation();
	IMemento Save();
	void Restore(IMemento memento);
}

interface ICaretaker
{
	IMemento Memento { get; set; }
}

class ConcreteMemento : IMemento
{
	private object _state;
	public void Save(object obj) => _state = obj;
	public object Restore() => _state;
}

class ConcreteOriginator : IOriginator
{
	private string _state;

	public string State
	{
		get => _state;
		set
		{
			_state = value;
			Console.WriteLine($"state := {value}");
		}
	}

	public void Operation() => State = State.ToUpper();

	public IMemento Save()
	{
		IMemento memento = new ConcreteMemento();
		memento.Save(State);
		return memento;
	}

	public void Restore(IMemento memento)
	{
		State = memento.Restore() as string;
	}
}

class ConcreteCaretaker : ICaretaker
{
	public IMemento Memento { get; set; }
}

public class Client
{
	public void Main()
	{
		IOriginator originator = new ConcreteOriginator();
		originator.State = "some text";

		ICaretaker caretaker = new ConcreteCaretaker();
		caretaker.Memento = originator.Save();

		originator.Operation();

		originator.Restore(caretaker.Memento);

		/**
		 * OUTPUT:
		 * state := some text
		 * state := SOME TEXT
		 * state := some text
		 */
	}
}