Any fool can write code that a computer can understand. Good programmers write code that humans can understand. - Martin Fowler
Legend:
- π promotes decoupled architecture
- π synergy with SOLID principles (ocp, srp, dip...)
βΆοΈ runtime focused (i.e: promotes reconfiguration)- π compiletime focused
- π configurable once (during initialization, usually from a config file)
- π creational pattern
- β structural pattern
π ±οΈ behavioral pattern- π controls death of an object
- π top pattern according to my experience
- β»οΈ often using when refactoring legacy code
understand it and then follow it at all costs... patters will come later...
creation of entities
πππ β»οΈ
code
| code
| wiki
When: We need a common operation that creates an object (single) without specyfying exact class object that will be created. Subclasses should decide which class to instantiate. When we need OOP replacement for switch
duplication.
Usage:
BaseUiFactory uiFactory = new MultithreadedUiFactory();
...
IWindow mainWindow = uiFactory.GetWindow(Environment.OsType);
mainWindow.Show(800, 600, "Factory method sample v1.0");
Keywords: switch
, harmful new
, factory class, SOLID open/closed, refactoring
Synergy with: template method (called within 'em), object pool
Competing with: abstract factory, builder
ππ
code
| code
| code
| wiki
When: A class needs to be independent of how families of objects it requires are created.
Usage:
class GameEngine {
...
GameEngine(IGuiFactory uiFactory, ...)
{
IGuiSplashscreen splash = uiFactory.CreateSplashscreen();
splash.Show("Loading...", 5s);
IGuiMainWindow window = uiFactory.CreateMainWindow(Mode.NoFrame);
m_mainThread = window.Show();
}
...
}
var engine = new GameEngine(new WpfGuiFactory(...), ...);
Keywords: IFactory
IProduct
, avoid new
Synergy with: dependency injection
Competing with: factory method
ππβ»οΈ
code
| code
| code
| wiki
When: We need to separate an construction of complex object from its representation allowing same construction process to create various representations. Provide control over steps of construction process.
Usage:
IWindowBuilder builder = ... some concrete builder;
...
builder.SetTitle("Yo").ForOs(OsType.Windows); // note: method chaining
if(isWpfEnv) builder.AttachTo(m_mainProcess);
SetSizeByDeviceDimensions(builder); // i.e dimensions 800x600
builder.ConfigureLogging(logging => { logging.AddFile("Logs/{Date}.log"); });
...
IWindow mainWindow = builder.Create();
mainWindow.Show();
Keywords: builder, complex creation, complex constructor, refactoring
Synergy with: method chaining, composite (built by builder)
Competing with: dependency injection
ππππππ
code | wiki
When: We want classes to remove all knowledge of a concrete implementations that they needs to use. When no client code has to be changed simply because an object it depends on needs to be changed to a different one. When we want to specify the way objects are created in separate configuration files. When we want to do concurrent or independent development, with two developers writing classes that use each other, while only needing to know the interface the classes will communicate through. When we want a special (injector) object to resolve the dependency tree and return fully resolved objects. When we need a plugin system. When we need independent entities that are easier to unit test in isolation using mock objects.
Usage:
class SdlEngine : IGameEngine {
private readonly IMyDependency m_myDependency;
Engine(IMyDependency dep, ILogger<Engine> logger, IConfiguration config) {
m_myDependency = dep;
...
}
public ISomeOptionalService SomeService { get; set; }
}
...
services.AddSingleton<typeof(ILogger<T>), typeof(Logger<T>)>();
services.AddScoped<IConfiguration, JsonFileConfig>();
services.AddTransient<IMyDependency>( sp => new SomeDynamicDependency() );
IGameEngine engine = services.Resolve<SdlEngine>();
Keywords: di container, ioc inversion of control, SOLID soc, ctor vs setter, plugins, plug-ins, unit testing
Synergy with: dependency injection container, registry
Competing with: service locator (injects implementations instead of a factory that resolves dependencies at runtime), abstract factory (doesn't have to delegate instantiation to a factory object), builder, template method (also an ioc but with composition over inheritance), strategy (injects dependencies during init instead of allowing interchangeable dependencies at runtime)
πππ
When: Same as in DI but we want to inject a factory that resolves dependencies at runtime. When we want to allow dependencies to be changed at runtime without re-compiling the application. When application should optimize itself at runtime by selectively adding and removing items from the service locator.
Usage:
class SdlEngine : IGameEngine {
private readonly IServiceLocator m_services;
Engine(IServiceLocator services) {
m_services = services;
}
void Run() {
IScene scene = m_services.Get<IScene>("layered");
while(true) {
IFrame engine = m_services.Get<IFrame>(scene, time);
...
}
}
}
...
services.AddSingleton<IScene, FastScene>();
services.AddSingleton<IScene, LayeredScene>("layered");
services.AddTransient<IFrame, FastFrame>();
IGameEngine engine = nw SdlEngine(services);
Keywords: ioc inversion of control, SOLID soc, inside a loop
Synergy with: ?
Competing with: dependency injection
π
code
| code
| code
| wiki
When: To delay the creation of a "heavy" object, the calculation of a value or some other expensive process until the first time it is used.
Usage:
Lazy<IGameEngine> lazyEngine = new Lazy<Engine>();
...
IGameEngine engine = lazyEngine.Value;
Keywords: delay, virtual proxy, ghost, lazy loading, lazy factory, value holder, performance
Synergy with: factory method, multiton, proxy (often uses lazy), singleton (uses lazy)
Competing with: ?
πππ
code
| code
| wiki
When: It is necessary to work with a large number of objects that are particularly expensive to instantiate and each object is only needed for a short period of time. Rather than allocating and destroying objects on demand, we care for performance and we want a set of recycling objects kept ready to use - aka "pool".
Usage:
var task = Task.Run(() => { ... }); // acquire expensive thread from the thread pool
task.Wait(); // after that, lets the thread get back to the thread pool
Keywords: object pooling, memory pool, thread pool, connection pool, recycling objects, performance, minimal memory footprints, acquire release
Synergy with: singleton (is often a singleton), factory method, dispose
Competing with: ?
ππ
code
| wiki
When: When exactly one globally available object instance is needed to coordinate actions across the system. When we want to restrict the instantiation of a class to one object. When implementing facade objects since usually only one facade object is required.
Usage:
GameEngine.Instance.NextFrame();
...
GameEngine.Instance.SetAbsolutePosition(car, 0, 0);
...
GameEngine.Instance.Draw();
Keywords: anti-pattern, one instance, global state, static, thread safety problem
Synergy with: facade, factory method, abstract-factory, builder, prototype, lazy initialization
Competing with: multiton
ππ
code
| wiki
When: We need N x singleton to manage a map of named instances (as key-value pairs) with global point of access.
Usage:
var foreground = GameScene.GetInstance(LayerType.Foreground);
var background = GameScene.GetInstance(LayerType.Background);
Keywords: anti-pattern, global state, static
Synergy with: lazy initialization
Competing with: singleton
ππ
code
| code
| cpde
| wiki
When: We want to signal for explicit call to a method which releases any resources the object is holding onto.
Usage:
using (GameResource resource = GetGameResource()) {
...
}
Keywords: resource management, close, dispose, free, cleanup, release, RAII
Synergy with: object pool
Competing with: ?
π
When: When the type of objects to create is determined at runtime by prototypical instance, which is cloned to produce new objects.
Usage:
bullet.SetPosition(1337, 6);
bullet.Rotate(90deg);
...
var followingBullet = bullet.Clone();
Keywords: harmful new
, proto, clone, true copy, deep copy, shallow copy, performance, minimal memory footprints
Synergy with: factory method, registry (aka prototype manager)
Competing with: ?
πππ
code | wiki
When: We want to ensure that resources are properly released by tying them to the lifespan of an object.
Usage:
std::mutex m;
void RedrawStage(...) {
std::lock_guard<std::mutex> lk(m); // RAII: mutex acquisition is initialization
auto buffer = std::make_unique<uint8_t[]>(stage.Width*stage.Height); // RAII: memory acquisition is initialization
...
} // deallocation happens here even if the function throws
Keywords: c++, rust, RAII, exception safety, Constructor Acquires Destructor Releases (CADRe), Scope-based Resource Management (SBRM)
Synergy with: dispose
Competing with: ?
relationship between entities, structure related logic is being internal to the structure - the structure gets affected by various changes and executes some actions as a consequence
βππβ»οΈ
code
| code
| wiki
When: We want to allow classes with incompatible interfaces to work together. When we have to convert the interface of a class into another interface clients expect.
Usage:
ISdlLayer sdlLayer = new SdlLayer();
IGameLayer adapted = new My2DGameLayer(sdlLayer);
adapted.Draw(scene);
Keywords: wrapper, translator, refactoring
Synergy with: dependency injection, delegation (may be implemented as)
Competing with: decorator (adapt vs add behavior), facade (adapts to one interface vs simplifies many), delegation (different intent)
βππ
When: We want to attach additional responsibilities to an object, dynamically at run-time, keeping the same interface. When we want to stack multiple responsibilities on top of each other (via wrapping).
Usage:
IGameLayer basic = new Layer2D();
basic.Add(bullet);
IGameLayer rotated = new RotatedLayer(basic, 90deg);
IGameLayer transmuted = new TransmutedLayer(rotated, [1,2,2]);
transmuted.Draw(scene);
Keywords: subclassing alternative, SOLID srp opc, mixins, refactoring
Synergy with: dependency injection, forwarding (may be implemented as)
Competing with: chain of responsibility (exactly one of the classes handles the request, not all), adapter (change responsibility vs change interface), mixin
βπ
When: An abstraction and its characteristics should be defined as separate class hierarchies, thus extended independently, achieving composition over inheritance. We need two layers of abstraction since both the abstraction hierarchy and hierarchy of its behaviors vary often. When components can be selected at run-time rather than compile-time.
Usage:
IEngineImpl box2d = new Box2DEngineImplementor();
IEngineImpl farseer = new FarseerEngineImplementor();
IPhysicEngine pysics = new TwoDimensionalPhysicsEngine( useBox2d ? box2d : farseer );
IBody car = pysics.CreateBody(); // i.e: calls IEngineImpl.GetConcreteBody();
Keywords: SOLID ocp srp, composition over inheritance, pimpl, impl handle body, GUI framework, refactoring
Synergy with: dependency injection, abstract factory (used to create and configure a particular bridge)
Competing with: adapter (different intent, used after design, not during), strategy (different intent), state (change imp along with its state)
βππ
When: We need tree structures where clients treat individual objects and compositions of objects uniformly. When objects should forward requests to their child components.
Usage:
IStageObject objA = new Bitmap(...);
IStageObject objB = new Sprite(...);
IStageObject layerA = new CompositeSprite();
layerA.AddChild(objA);
layerA.AddChild(objB);
...
IStageObject mainLayer = new CompositeSprite();
mainLayer.AddChild(layerA);
...
mainLayer.Show(); // = spriteA.Show() & spriteB.Show();
Keywords: tree structure, part-whole hierarchy, node element, node leaf, mixin, XQuery
, XDocument
, refactoring
Synergy with: builder, decorator (used to decorate components)
Competing with: ?
βππ
code
| wiki
When: We want to provide a simplified/unified interface to a set of interfaces/libraries in a subsystem (i.e: OpenGL). When we want to wrap poorly designed collection of APIs.
Usage:
var facade = new OpenGlEsFacade();
facade.CreateComplete2DScene();
Keywords: forwarding, multiple interfaces hidden, monolit refactor, simplify api
Synergy with: singleton
Competing with: decorator, adapter, abstract factory
βπ
code | wiki
When: We want to filter a set of objects using different criteria and chaining them in a decoupled way through logical operations.
Usage:
IMatchCriteria sameRank = new RankCriteria(Rank.Diamond);
IMatchCriteria sameKdRatio = new KillDeathRatioCriteria(player.KDRatio);
var opponents = Criteria.And(sameRank, sameKdRatio).Filter(players);
Keywords: filter, criteria, matching
Synergy with: ?
Competing with: ?
β
code
| code
| code
| wiki
When: We want to minimize memory usage by sharing as much (invariant) data as possible with other similar objects. When we can afford to remove non-shareable state from the class, and having the client supply it when methods are called.
Usage:
class DirtTile { // the client of texture (flyweight), he is the he heavyweight one...
public int X {get; set}
public int Y {get; set}
public Matrix<2> Scale {get; set}
private ITexture texture = TextureFactory.Get("dirt"); // the flyweight with intrinsic (shared) bitmap data
public void DrawOnto(IScene scene) {
scene.ClearRectArea(X, Y, texture.Width, texture.Height, Scale);
texture.DrawOnto(scene.GetCanvas(), X, Y, Scale); // extrinsic data passed in
...
var dirtTiles = Enumerable.Range(0, 640).Random(200).Select( x => new DirtTile { X = x, Y = (x % 13) } );
dirtTiles.ForEach( tile => tile.DrawOnto(scene));
Super simplified case without factory but with shared invariant state:
class MyGlyph {
public Guid Id { get; set; }
public string FontName => MyFont.Name; // flyweight pointer to a string
public string License => MyFont.License; // flyweight pointer to a large string
public IList<int> Points = ...
}
Keywords: performance, memory usage, smart_ptr
, immutable shared data, intrinsic extrinsic, string interning
Synergy with: singleton, multiton, factory method, state (states are often flyweights), strategy (stategies are often flyweights)
Competing with: object pool (flyweights are shared among clients, objts from obj pool are not)
βππ
code | wiki
When: We want an object to delegate task to an associated helper object (with delegate using sender's context) instead of performing one of its stated tasks. When we want to achieve composition over inheritance.
Usage:
class Rectangle {
...
GetDimensions(IWindow receiver) => [Width + 2*receiver.BorderSize, Height + 2*receiver.BorderSize];
}
class Window : IWindow {
public int BorderSize { get; set; } = 1;
...
GetDimensions() => m_myDimesionsDelegate.GetDimensions(this);
}
...
var window = new Window("Some game", new Rectangle(100px, 100px));
...
var dimensions = window.GetDimensions(); // prints [102, 102]
class Window : IWindow {
public int BorderSize { get; set; } = 1;
...
Func<Rectangle> Dimensions = () => [Width + 2 * this.BorderSize, Height + 2 * this.BorderSize];
}
...
var window = new Window("Some game", 100px, 100px);
var dimensions = window.Dimensions(); // prints [102, 102]
Keywords: composition over inheritance, wrapper wrappee, sending receiving, functional programming
Synergy with: adapter, facade
Competing with: forwarding
βππ
code | wiki
When: We want an object to delegate task to an associated helper object (with delegate using its own context) instead of performing one of its stated tasks. Same use case as in delegation pattern with difference in binding of the this
(self, context) in the delegate when called by the original (sending) object.
Usage:
class Rectangle {
...
GetDimensions(IWindow receiver) => [Width, Height];
}
class Window : IWindow {
public int BorderSize { get; set; } = 1;
...
GetDimensions() => m_myDimesionsDelegate.GetDimensions() + 2*[BorderSize, BorderSize];
}
...
var window = new Window("Some game", new Rectangle(100px, 100px));
...
var dimensions = window.GetDimensions(); // prints [102, 102]
Keywords: composition over inheritance, wrapper object, wrapper function, wrapper wrappee, functional programming
Synergy with: adapter, facade
Competing with: delegation
βππ
code
| code
| code
| wiki
When: We want to controll access to another object. When we want to add extra functionality to an object like caching, audits, locking/synchronization or checking preconditions while hiding info about the real object.
Usage:
ISearchFeature search = new MetadataSearch();
search.OnDoc(somePdf, keywordList);
... refactor ...
ISearchFeature search = new ProxyMetadataSearch(Thread.CurrentPrincipal, checkLicenseOnline = true, retentionPolicy = 1min);
search.OnDoc(somePdf, keywordList);
Keywords: surrogate, forwarding, virtual proxy, access control, cache, smart pointer, SOLID srp
Synergy with: forwarding, lazy initialization
Competing with: decorator(adds functionalities vs controll access)
β
When: We need to associate metadata with a class, i.e: via empty interface. When wee need to mark a class.
Usage:
public interface IQuery { }
public interface ICommand { }
...
var request = requests.Dequeue();
if(request is IQuery) {
...
} else if (request is ICommand) {
...
Keywords: marker, tagging, custom attribute, annotation, reflection, run-time, Serializable
Synergy with: ?
Competing with: attributes
βπ π
code
| wiki
| wiki
When: We need one entity that groups several related elements, such as classes, singletons, services, methods etc. and simplify the process of their configuration and deployment. When we need to decreased configuration complexity (i.e: of di containers, automappers, DAL, logging). When we want group of services to configure themselves to their execution environment.
Usage:
var Screen = MainScreenModule.Get( cfg => cfg.ForceFullScreen().BringFront().DebugMode(true).ShowFPS() );
Screen.Load();
...
Screen.Display(someBitmap);
...
var position = Screen.LastTouch.GetLocation();
Screen.Unload();
Keywords: modular programming, bundle, bootstrap, subsystem, package, plug-in, not namespace
Synergy with: singleton, facade, flyweight, adapter
Competing with: namespace (no initializer/finalizer function, like ctor/dtor), dependency injection (doesn't work well with it, but may be used for di container configuration)
βπ
When: We need to register & access objects at runtime from anyewhere in the code via key-to-object registry.
Usage:
ICutscene carCrash = new CarCrashCutscene(...);
...
cutseneRegistry.Register("car-crash-cutscene", carCrash);
cutseneRegistry.Register("death-cutscene", new DeathCutscene(...));
cutseneRegistry.Register<EndCutscene>("ending-cutscene");
...
var ending = cutseneRegistry.Get("ending-cutscene");
ending.Play(game);
Keywords: IViewRegistry
, global, DI Container
Synergy with: dependency injection (as a di container), singleton
Competing with: multiton
πβ
When: We need multiple inheritance in a programming language that do not support this feature.
Usage:
Keywords: TODO
Synergy with: TODO
Competing with: TODO
behaviour between entities, scenarios external to the structures - a certain data structure can "be used" in multiple scenarios
When: We need an object oriented version of if ... else if ... else if ... else ... endif
that can be rearranged at run-time, with more than one object handling the request depending on run-time conditions.
Usage:
IEnemyGenerator basic = new RandomBasicEnemyGenerator(rng=...);
IEnemyGenerator elite = new RandomSpecialEnemyGenerator(rng=..., difficulty, next=basic);
IEnemyGenerator enemyGenerator = new DirectedEnemy(map, difficulty, next=elite);
enemyGenerator.AddEnemyToThe(scene);
Keywords: sender handler receiver, SOLID srp, asp.net middleware, linked list, pipeline
Synergy with: dependency injection
Competing with: decorator (all handle the request vs only one)
When: We need an object that encapsulates all information needed to perform an action or trigger an event at a later time. When we need to decouple invoker (of the command) and the handlers (of the command). When we need to save requests in a queue.
Usage:
IGamePlayer player = ...
IGameEngine game = ...
ICommand killPlayerCommand = new DelegateCommand<CauseOfDeathEnum>(
causeOfDeath => game.IsRunning() && player.IsAlive() && player.CanBeKilledVia(causeOfDeath),
causeOfDeath => player.KillVia(causeOfDeath))
//or ICommand killPlayerCommand = new KillCommand(player);
IScenario scenario = RussianRoulette(game, killPlayerCommand);
// i.e:
// if(killPlayerCommand.CanExecute(CauseOfDeathEnum.ShotInTheHead))
// killPlayerCommand.Execute(CauseOfDeathEnum.ShotInTheHead);
scenario.Run();
Keywords: command receiver invoker client, mvc, mvvm, delayed execution, encapsulation, SOLID srp ocp, DelegateCommand
, RelayCommand
, CompositeCommand, GUI, refactoring
Synergy with: memento (can pass state for ops like undo), delegate (coz Execute()
delegates the call to the handler)
Competing with: servant
When: We need decent separation of concerns via defining what is an command that performs an action (mutate or modify the data), or a query that returns data to the caller (safely retrieve data), but never both. When we want to constrain that methods should return a value only if they create no side effects.
Usage:
IQuery query = new GetPlayerRankQuery { PlayerID = playerId, ShowForCurrentSeason = true };
IRankQueryResult result = queryDispatcher.Dispatch<IRankQueryResult>(query); // finds & fires appropriate query handler
if(result.Rank == LadderRank.Diamond) {
...
ICommand updateCommand = new UpdateRankCommand{ PlayerID = playerId, KdRatio = 0.7, OpponentRank = ... };
IRankCommandResult result = commandDispatcher.Dispatch<IRankCommandResult>(updateCommand); // finds & fires appropriate command handler
if(result.Status == CommandResult.OK) {
...
Keywords: principle, design by contract, not CQRS, complex domains, DDD, separation of concerns, modular, Query
QueryResult
QueryDispatcher
QueryHandler
, Command
CommandResult
CommandDispatcher
CommandHandler
Synergy with: CQRS for event-based architectures
Competing with: CRUD
When: We need to evaluate sentences in a language. When we need to interpret expressions.
Usage:
var context = new GameCmdContext();
var expressions = expressionParser.Parse(console.GetText(), context);
expressions.Interpret(context);
// or: for( var expr in expressions) expr.Interpret(context);
scenario.Perform(context.Action, context.Params);
Keywords: context expression terminal
Synergy with: composite, state
Competing with: ?
When: We need a way to access the elements of an aggregate object sequentially e.g. when data has to be be lazy-loaded or to avoid memory allocation peaks.
Usage:
IEnumerator<Sprite> walking = spriteManager.GetEnumeratorFor(PlayerSpriteEnum.Walking);
...
walking.Reset();
while(walking.MoveNext())
{
scene.Draw(walking.Current);
...
}
Keywords: container, enumerator, IAsyncEnumerable
IEnumerable<T>
IEnumerator<T>
, lazy loading, internal vs external, functional programming (internal iterator)
Synergy with: composite
Competing with: ?
When: We want to notify/update other (multiple) objects about state change within an object. When multiple objects are dependent on the state of one object. When we want to register and unregister from notifications at runtime.
Usage:
IObservable<Connection> gamePortHub = ...
IObserver<Connection> gameLobby = ...
IDisposable unsub = gamePortHub.Subscribe(gameLobby);
...
gamePortHub.Connected(new Connection{...});
...
unsub.Dispose();
PlayerJoinedLobby(object source, ConnectedEventArgs e) { ... }
...
player.AddEventListener("connected", PlayerJoinedLobby)
player.AddEventListener("connected", (s, e) => { chat.Send("Player {e.Name} joined the lobby."); }) // leak, use wak ref
...
player.RemoveEventListener("connected", PlayerJoinedLobby)
// EventHandler<ConnectedEventArgs> is a delegate type, ConnectedEventArgs is event data
public event EventHandler<ConnectedEventArgs> Connected;
...
// Connected is an event
Connected += (s) => {...};
...
OnConnected(...)
{
Connected?.Invoke(this, new ConnectedEventArgs{...})
}
Keywords: one-to-many, subject observers, publish subscribe, event listener, event emitter target dispatcher, event driven, memo leak, weak reference, message handler, IObservable<T>
IObserver<T>
, view in mvc, viewmodel, push vs pull architecture, refactoring
Synergy with: dispose
Competing with: mediator
When: We have lots of objects that are potential event sources. When we have multiple publishers for multiple subscribers.
Usage:
IEventAggregator eventAggr = game.EventAggregator;
var subToken = eventAggr.GetEvent<GameStateSaveEvent>()
.Where( e => e.IsClosing ).Subscribe( e => stateManager.Save(e.State) ).ToToken();
using(var subSession = eventAggr.Subscribe<GameStateSaveEvent>( e => stateCacheManager.Cache(e.State) )
{
...
eventAggr.Publish(new GameStateSaveEvent { Sate = ecs.Dump(), IsClosing = true });
}
...
eventAggr.Ubsubscribe(subToken);
Keywords: many-to-many, dispatcher, publishers subscribers, event bus, message hub, change manager, IObserver<T>
IObservable<T>
Synergy with: observer (more advanced version of an observer), dispose (for unsub), singleton (often is a singleton)
Competing with: observer (for less complex scenarios)
When: We want classes to exchange messages between each other but not directly but via specialized mediator class. We do not want 'em to communicate/interact directly with each other. When we want to reduce coupling between (set of) classes. When we want to define exactly how they can interact.
Usage:
// TODO: needs simpler example
IModalMediator modalMediator = new AsyncModalMediator(InvokeOn.MainUiThread);
modalMediator.RegisterView<FilterOptionsViewModel, FilterOptionsView>();
...
class ShellViewModel {
ShellViewModel(IModalMediator modalMediator, ... ) {
modalMediator.RegisterContainerForModals(this);
...
}
...
void ShowModal(IView view, IViewModel vm) {
ActiveModal = view;
view.Closed += (s, e) => modalMediator.Closed(vm);
...
class ItemListViewModel {
...
ItemListViewModel(IModalMediator modalMediator, ... ) {
this.modalMediator = modalMediator;
}
...
async Task ShowFilterOptions() {
var filtersVm = new FilterOptionsViewModel();
await modalMediator.ShowAsync(filtersVm);
...
Keywords: sender receiver
Synergy with: observer (mediator can be also an observer), command (passed between objs via mediator)
Competing with: observer (same intent, receivers reconfigured at runtime, doesn't encapsulate the communication between other objs), event aggregator (same intent, many-to-many, not only messages but events, does not have business/workflow logic), chain of responsibility (passes a sender request along a chain of potential receivers VS has senders and receivers reference each other indirectly)
When: We want to have an ability to restore an object to its previous state.
Usage:
GameState save = ecs.Dump();
autoSave.Add(save);
...
ecs.Load(autoSave.GetLast());
Keywords: save state, undo via rollback, originator caretaker memento, check point, immutable memento
Synergy with: command (for undo)
Competing with: command (passes request rather than state)
When: We want to invoke multiple method calls in single statement, without requiring variables to store the intermediate results.
Usage:
IEnumerable<string> monsterIDs = stageObjectDict
.Where(x => x.Value.IsMonster && x.Value.IsVisible)
.OrderBy(x => x.Value.AppearTime)
.Select(x => x.Key.ToUpper());
Keywords: named parameter idiom, cascading-by-chaining by returning this, method cascading, LINQ, functional programming
Synergy with: builder, method cascading, fluent interfaces
Competing with: ?
When: We want to avoid null references and runtime NullReferenceException
s by providing a default object. When we want to pass/return a an object which implements the expected interface, but whose method body is empty. When we need to remove old functionality by replacing it with null objects (refactoring).
Usage:
if(fake)
enemyId = String.Empty; // ex #1
...
enemy = enemiesOnStage.Where( x => x.Id.Equals(enemyId)).GetFirstOrDefault(); // ex #2
enemy.AttachTo(stage);
Keywords: SOLID lsp, nullable, default, empty list, null coalescing, LINQ, insert null object refactoring, refactoring, functional programming, Nullable<T>
, T?
, string.Empty
, std::optional
Synergy with: state (can be returned as default), singleton, factory method (return real or null obj), template method (for concrete Template that does nothing)
Competing with: ?
When: We need centralized, runtime configuration.
Usage:
IList<IFighter> fighters = AssetManager.LoadFrom<IFighter>("./data/fighters");
Keywords: runtime, add-on, extension, plug-in, separated interface
Synergy with: ?
Competing with: ?
When: We need to encapsulate the set of objects persisted in a data store and the operations performed over them, providing a more object-oriented view of the persistence layer (more like a collection). When we need testable/mockable mediator between the domain/business logic and data access/mapping layers. Not a database abstraction!
Usage:
PlayerStats stat = statsRepo.Find(playerName, new StatFilter{ IsBeta = true });
stat.GamesPlayed++;
playerStatsRepo.CreateOrUpdate(stat);
...
statRepo.Delete(player);
orderRepo.ClearHistory(playerId); // note: shouldn't be in DDD
playerRepo.SetInactive(player); // note: shouldn't be in DDD
Keywords: CRUD, IQueryable<T>
vs IEnumerable<T>
, paging, sorting, lazy loading, IAsyncEnumerable
, unit-of-work SaveChanges
Synergy with: unit-of-work, fluent interfaces
Competing with: ORM (DbSet is a repo)
When: We want many small database updates that represent one business transaction to merge into single batch to optimize the number of round-trips.
Usage:
// repositories and the uow as separate objects with uow injected into repositories
statRepo.Delete(playerId);
orderRepo.ClearHistory(playerId);
playerRepo.SetInactive(player);
trx.Commit();
// repositories being exposed via uow with uow composed of repositories
uow.StatRepo.Delete(playerId);
uow.OrderRepo.ClearHistory(playerId);
uow.PlayerRepo.SetInactive(player);
uow.Commit();
Keywords: database transaction, business transaction, unit-of-work, SaveChanges
, commit, rollback
Synergy with: repository
Competing with: ORM (DbContext is a uow), Saga (distributed systems)
When: We want a task to be scheduled to be performed at a particular interval or clock time.
Usage:
Keywords: real-time computing
Synergy with: TODO
Competing with: TODO
When: We want to define common functionality for a group of classes. When we need an object to which we want to offer some functionality, so that it can perform it (serve) on objects (passed as parameters) at a later time.
Usage:
IGameEngine game = ...
IKillServant hitman = new HitmanServant(game);
...
IKillable victim = player as IKillable;
hitman.TryToKill(victim);
hitman.KillWith(ToolboxEnum.Crowbar, victim);
HitmanHelper.KillWith(ToolboxEnum.Crowbar, this as IKillable);
Keywords: helper class, utility class, all static methods, no encapsulation, no SOLID srp ocp
Synergy with: ?
Competing with: command
When: We need easily maintainable, yet highly customizable business logic. When business rules should be recombined an run-time by chaining the rules together using boolean logic.
Usage:
ISpec<Invoice> OverDue = new OverDueSpecification();
ISpec<Invoice> NoticeSent = new NoticeSentSpecification();
ISpec<Invoice> InCollection = new InCollectionSpecification();
ISpec<Invoice> ShouldBeSendToCollection = OverDue.And(NoticeSent).And(InCollection.Not());
foreach (var invoice in service.GetInvoices()) {
if (ShouldBeSendToCollection.IsSatisfiedBy(invoice)) {
...
Keywords: anti-pattern, DDD domain-driven design, business rules, runtime, BDD behavior-driven development, SpecFlow
Synergy with: interpreter, composition
Competing with: ?, boolean algebra
When: An object has to have many behaviours which change depending on its internal state. Behaviour often change at run-time.
Usage:
public class Player : GameObject {
private IPlayerState _state;
...
public override void Initialize() {
_state = new PlayerIdleState();
}
public override void Update(GameTime gameTime) {
HandleInput(Keyboard.GetState());
UpdateState(gameTime);
Animation.Update(gameTime);
}
private void UpdateState(GameTime gameTime) {
if (_state != null) {
IPlayerState newState = _state.Update(this, gameTime);
if (newState != null && _state != newState) {
_state.Exit(this);
_state = newState;
_state.Enter(this);
...
class PlayerAttackState : IPlayerState {
...
public IPlayerState Update(IPlayer player, GameTime gameTime) {
if (player.Animation.AnimationCompleted)
return new PlayerIdleState();
return null;
...
Keywords: SOLID srp ocp, finite state machine, refactoring (status/state field and many if
s)
Synergy with: flyweight (states often are flyweights)
Competing with: strategy (states often change internaly)
When: Behavior (algorithm, strategy) of a class needs to be selectable (or interchangeable) without breaking the class that use it, both at run-time as well as at design-time.
Usage:
public class EliteZombie : IMonster, IGameObject, IElitare {
IMonsterBehavior Behavior = MonsterBehavior.Default;
...
Update(GameTime gameTime) {
Behavior.UpdateVelocity(this.Body);
if(Behavior.ShouldAttack(this.Body.Position, this.AttackRange)) {
...
}
...
}
monster[i].SetBehavior(new NormalUnawareMonsterBehavior(game));
monster[i].AttachTo(stage);
...
if(monster.CanSee(player)) {
monster.SetBehavior(new AwareButStealthMonsterBehavior(game, player, fadeOut: true));
}
else if(monster.Health/monster.MaxHealth < 0.4) {
monster.SetBehavior(new EnragedMonsterBehavior(game, player));
}
Keywords: SOLID srp ocp, composition over inheritance, policy pattern, refactoring (many if
s with different behaviours), inversion of control IoC, high cohesion, loose coupling
Synergy with: factory method (to hide concrete imps and decide which strategy to use), dependency injection, flyweight (strategies often are flyweights)
Competing with: state (strategies often are changed by external source), template (also IOC but achieved via inheritance, not via delegation), bridge (same diagram, different intent: behavior vs structure)
When: We have overall behaviour (algorithm) of a class defined, but we want some of the steps to be redefinable by the subclasses.
Usage:
abstract class TrainingAlgorithmBase : ITrainingAlgorithm {
abstract void PreTrain();
abstract ErrorGradient CalculateEpoch(ITraininDataSet set);
abstract void PostTrain();
...
void Train(IFeedForwardNetwork network, ITraininDataSet set) {
PreTrain();
for (int i = 0; i < maxIterations; ++i) {
var errorGrad = CalculateEpoch(set);
if(IsBelowThreshold(errorGrad))
break;
errorGrad = EludeLocalMinima(network, errorGrad)
}
PostTrain();
...
class SupervisedTraining : TrainingAlgorithmBase {
override void PreTrain() {
...
var trainer = new SupervisedTraining( optimizationAlgorithm, 1000, 0.00001 );
trainer.Train(network, training_set);
Keywords: inversion of control IoC, refactoring, inheritance, override, customization hooks, prefix do- pre- post-, frameworks
Synergy with: factory method (often used within)
Competing with: dependency injection, strategy (changes whole algo, not parts of it, delegation vs inheritance)
When: We want to enable adding new behaviours (operations, algorithms) that operate on some class hierarchy/fammily of classes (object structures) without the need to change 'em. When we want behaviours to be separated from the object structures on which they operate. When new ops are added frequently. When the classes that make up the object structure are known and not expected to change. When recovering lost type information without resorting to dynamic (down)cast (dynamic_cast<PersianCat*>(animal)
, (PersianCat)animal
, animal as PersianCat
).
Usage:
struct NoTarget{};
using AggroResult = std::variant<PlayerID, MobID, NoTarget, Error>;
...
const std::vector<AggroResult> targets = boss.ChooseTargets<3>();
...
for (const auto& target: targets) {
std::visit(overloaded {
[](auto _) { /* nop */ },
[](const Error& e) { log.Error(e.Code, e.Reason.ToString()) },
[](PlayerID player) { if(boss.currentTarget != player) boss.AttackNext(player); },
[](MobID mob) { boss.Heal(mob); },
}, target);
}
Keywords: SOLID ocp, method overload, double dispatch, obj.accept(visitor)
-> visitor.visit(obj)
, dynamic_cast<>
, refactoring
Synergy with: composite (object structures can be a composite structure)
Competing with: ?
- https://en.wikipedia.org/wiki/Software_design_pattern#Concurrency_patterns
- https://en.wikipedia.org/wiki/Concurrency_pattern
- https://www.martinfowler.com/eaaDev/
- http://wiki.c2.com/?SoftwareDesignPatternsIndex
- http://wiki.c2.com/?CategoryPattern
- https://sourcemaking.com/design_patterns
- https://refactoring.guru/design-patterns/catalog
- https://prismlibrary.github.io/docs/wpf/legacy/Appendix-B-Patterns.html
- http://gameprogrammingpatterns.com/contents.html
- https://www.tutorialspoint.com/design_pattern/index.htm
- https://www.oodesign.com/design-principles.html
- https://www.dofactory.com/net/design-patterns
- https://www.geeksforgeeks.org/software-design-patterns/
- https://springframework.guru/gang-of-four-design-patterns
- https://www.modernescpp.com/index.php/design-patterns-and-architectural-patterns-a-first-overview
πβ
When: TODO
Usage:
Keywords: TODO
Synergy with: TODO
Competing with: TODO
Git Gud or Die Tryin'