Gof.DesignPatterns
2.0.2
dotnet add package Gof.DesignPatterns --version 2.0.2
NuGet\Install-Package Gof.DesignPatterns -Version 2.0.2
<PackageReference Include="Gof.DesignPatterns" Version="2.0.2" />
<PackageVersion Include="Gof.DesignPatterns" Version="2.0.2" />
<PackageReference Include="Gof.DesignPatterns" />
paket add Gof.DesignPatterns --version 2.0.2
#r "nuget: Gof.DesignPatterns, 2.0.2"
#:package Gof.DesignPatterns@2.0.2
#addin nuget:?package=Gof.DesignPatterns&version=2.0.2
#tool nuget:?package=Gof.DesignPatterns&version=2.0.2
Gof.DesignPatterns
Gof.DesignPatterns is a .NET class library of generic abstractions for a subset of the Gang of Four (GoF) design patterns — those that can be expressed as reusable interfaces (and a few abstract bases).
The package is intended to speed up application code by giving you a shared vocabulary and type-safe contracts. You still write the domain-specific implementations. It is not a complete GoF catalog and not a drop-in replacement for a textbook “one class per pattern” sample.
Supported targets: netstandard2.0, netstandard2.1, net8.0, net9.0, net10.0 (C# 14, nullable reference types enabled).
Content
- Disclaimer
- Installation
- What this library provides
- Creational patterns
- Structural patterns
- Behavioral patterns
- Concurrent APIs
- Agent skill
- Resources
- Contributing
- License
Disclaimer
Code and opinions in this repository are the author’s personal work, created in spare time. They do not represent the views of any employer and are not affiliated with, supported by, or funded by any employer.
Installation
Latest stable package: Gof.DesignPatterns 2.0.2 on NuGet.
dotnet add package Gof.DesignPatterns --version 2.0.2
Package Manager:
Install-Package Gof.DesignPatterns -Version 2.0.2
What this library provides
| Surface | Role |
|---|---|
DesignPatterns |
Synchronous interfaces (IStrategy, IAdapter, …) |
DesignPatterns.Implementation |
Shared bases: SingletonBase<T>, DistributiveBuilderBase<T>, CompositeBase, ChainOfResponsibilityBase<TResult, TOperation> |
DesignPatterns.Concurrent |
Async counterparts (ExecuteAsync / HandleAsync / Task-based APIs) |
Included patterns
- Creational: Abstract Factory, Builder, Prototype, Factory Method, Singleton
- Structural: Adapter, Bridge, Composite
- Behavioral: Chain of Responsibility, Command, Strategy, Visitor
Patterns without a generic abstraction in this repo (for example Observer or Decorator) are intentionally omitted.
Creational patterns
Creational patterns isolate how objects are created so callers depend on contracts rather than concrete construction.
Abstract Factory
Intent. Provide an interface for creating objects without binding callers to concrete types. In the GoF book, a factory typically exposes several methods that create a family of related products.
In this library. IAbstractFactory<TResult> and IAbstractFactory<TResult, TInput> model one product role per interface. The non-generic IAbstractFactory.Create<TResult, TInput> is a type-parameterized factory: the caller chooses both types at the call site. For a textbook family (for example CreateButton() + CreateScrollBar()), define your own interface with one member per product.
using DesignPatterns;
public sealed class Company
{
public required string Name { get; init; }
}
public sealed class CompanyFactory : IAbstractFactory<Company, string>
{
public Company Create(string operationData) => new() { Name = operationData };
}
IAbstractFactory<Company, string> factory = new CompanyFactory();
Company company = factory.Create("Contoso");
Builder
Intent. Separate construction of a complex object from its representation so the same process can produce different results.
In this library. IBuilder<TResult> is a single Build() step. IDistributiveBuilder<TResult> (and DistributiveBuilderBase<TResult>) registers an ordered list of mutations. With safely: true, a mutation that throws is skipped instead of aborting the chain.
using DesignPatterns;
using DesignPatterns.Implementation;
public sealed class Order
{
public string Customer { get; set; } = "";
public decimal Total { get; set; }
}
public sealed class StaticReportBuilder : IBuilder<string>
{
public string Build() => "monthly-report";
}
var order = new DistributiveBuilderBase<Order>()
.AddMutation(o => o.Customer = "Ada")
.AddMutation(o => o.Total = 19.99m)
.Build();
Prototype
Intent. Create new objects by copying a prototypical instance rather than constructing from scratch.
In this library. IPrototype<TThis>.Clone() — you decide shallow vs deep copy.
using DesignPatterns;
public sealed class Document : IPrototype<Document>
{
public required string Title { get; init; }
public Document Clone() => new() { Title = Title };
}
var original = new Document { Title = "Spec" };
Document copy = original.Clone();
Factory Method
Intent. Define an interface for creating an object, and let subclasses (or implementations) decide the concrete type.
In this library. IFactoryMethod<TResult>.Instantiate().
using DesignPatterns;
public sealed class Widget { }
public sealed class WidgetFactory : IFactoryMethod<Widget>
{
public Widget Instantiate() => new Widget();
}
Widget widget = new WidgetFactory().Instantiate();
Singleton
Intent. Ensure a type has one shared instance and a single access point.
In this library. ISingleton<TThis>.GetInstance() and SingletonBase<TThis> (where TThis : new()). The instance is stored in a static field (eager, thread-safe via .NET type initialization). The new() constraint means the language cannot forbid new TThis(); treat GetInstance() as the intended entry point. GetInstance is an instance method: construct a throwaway host and ask it for the shared instance.
using DesignPatterns.Implementation;
public sealed class ConfigurationStore : SingletonBase<ConfigurationStore>
{
public string Environment { get; set; } = "Production";
}
var first = new ConfigurationStore().GetInstance();
var second = new ConfigurationStore().GetInstance();
// first and second are the same object
Structural patterns
Structural patterns compose classes and objects into larger structures without making them rigid.
Adapter
Intent. Convert one interface into another so otherwise incompatible types can work together.
In this library. IAdapter<TSource, TResult>.Adapt(TSource) converts a separate source object. IAdapter<TResult>.Adapt() is for the case where the implementing type is the source and adapts itself.
using DesignPatterns;
public sealed record Customer(string Name);
public sealed record Company(string CompanyName);
public sealed class CustomerToCompanyAdapter : IAdapter<Customer, Company>
{
public Company Adapt(Customer source) => new(source.Name);
}
Company company = new CustomerToCompanyAdapter().Adapt(new Customer("Ada"));
Self-adapting form:
using DesignPatterns;
public sealed class LegacyCustomer : IAdapter<Company>
{
public required string Name { get; init; }
public Company Adapt() => new(Name);
}
Company adapted = new LegacyCustomer { Name = "Ada" }.Adapt();
The DesignPatterns.Concurrent namespace mirrors both shapes with IAdapter<TResult>.AdaptAsync() and IAdapter<TSource, TResult>.AdaptAsync(TSource).
Bridge
Intent. Split an abstraction from its implementation so the two can vary independently.
In this library. IBridge<TImplementation> exposes Implementer. Domain operations on the abstraction delegate to that implementer.
using DesignPatterns;
public interface IRenderEngine
{
string RenderCircle(int radius);
}
public sealed class SvgRenderEngine : IRenderEngine
{
public string RenderCircle(int radius) => $"<circle r=\"{radius}\" />";
}
public sealed class CircleBridge : IBridge<IRenderEngine>
{
public CircleBridge(IRenderEngine implementer, int radius)
{
Implementer = implementer;
Radius = radius;
}
public IRenderEngine Implementer { get; }
public int Radius { get; }
public string Draw() => Implementer.RenderCircle(Radius);
}
string svg = new CircleBridge(new SvgRenderEngine(), 10).Draw();
Composite
Intent. Compose objects into a tree and treat leaves and nodes uniformly.
In this library. IComposite (Add, Remove, Clear, IsComposite) and CompositeBase (in-memory list). IsComposite() is true when the node has children.
using DesignPatterns;
public sealed class Node(string name) : CompositeBase
{
public string Name { get; } = name;
}
var root = new Node("Root");
var child = new Node("Child");
root.Add(child);
bool hasChildren = root.IsComposite(); // true
root.Remove(child);
Behavioral patterns
Behavioral patterns assign responsibilities and shape communication between objects.
Chain of Responsibility
Intent. Pass a request along a chain of handlers until one handles it.
In this library. IChainOfResponsibility<TResult, TOperation> and ChainOfResponsibilityBase<TResult, TOperation>. Each handler has at most one successor. AddNextHandler registers the next link and returns that next handler so you can continue chaining from the tail. RegisterNext replaces the current successor.
using DesignPatterns.Implementation;
public sealed class PositiveHandler : ChainOfResponsibilityBase<string, int>
{
public override string Handle(int operationData)
{
if (operationData > 0) return "Positive";
return Next is not null ? Next.Handle(operationData) : "Unknown";
}
}
public sealed class NegativeHandler : ChainOfResponsibilityBase<string, int>
{
public override string Handle(int operationData)
{
if (operationData < 0) return "Negative";
return Next is not null ? Next.Handle(operationData) : "Unknown";
}
}
var head = new PositiveHandler();
head.AddNextHandler(new NegativeHandler());
string sign = head.Handle(-3); // "Negative"
Command
Intent. Encapsulate a request as an object so you can parameterize, queue, or log operations.
In this library. ICommand (Execute()), ICommand<TResult>, and ICommand<TResult, TExecutor> (execute against a collaborator).
using DesignPatterns;
public sealed class Ledger
{
public int Balance { get; set; }
}
public sealed class CreditCommand : ICommand<Ledger, int>
{
private readonly Ledger _ledger;
public CreditCommand(Ledger ledger) => _ledger = ledger;
public Ledger Execute(int amount)
{
_ledger.Balance += amount;
return _ledger;
}
}
var ledger = new CreditCommand(new Ledger()).Execute(50);
Strategy
Intent. Encapsulate algorithms and make them interchangeable inside a context.
In this library. IStrategy, IStrategy<TResult>, and IStrategy<TResult, TData>.
using DesignPatterns;
public sealed class UpperCaseStrategy : IStrategy<string, string>
{
public string Execute(string data) => data.ToUpperInvariant();
}
public sealed class LowerCaseStrategy : IStrategy<string, string>
{
public string Execute(string data) => data.ToLowerInvariant();
}
IStrategy<string, string> strategy = new UpperCaseStrategy();
string text = strategy.Execute("GoF");
Visitor
Intent. Add operations to a type hierarchy without modifying the element types, via double dispatch (Accept / Visit).
In this library. IAcceptor / IVisitor and IAcceptor<TResult> / IVisitor<TResult>. Concrete acceptors typically dispatch to a more specific visitor interface when available.
using System;
using DesignPatterns;
public sealed class StringAcceptor(string value) : IAcceptor<string>
{
public string Value { get; } = value;
public string Accept(IVisitor<string> visitor) => visitor.Visit(this);
}
public sealed class UpperCaseVisitor : IVisitor<string>
{
public string Visit(IAcceptor<string> acceptor) => acceptor switch
{
StringAcceptor s => s.Value.ToUpperInvariant(),
_ => throw new NotSupportedException(acceptor.GetType().Name)
};
}
string result = new StringAcceptor("gof").Accept(new UpperCaseVisitor());
Concurrent APIs
DesignPatterns.Concurrent mirrors the same pattern set with asynchronous methods (Task / Task<T>). Use these when the work is I/O-bound or already async in your application.
using DesignPatterns.Concurrent;
public sealed class UpperCaseStrategy : IStrategy<string, string>
{
public Task<string> ExecuteAsync(string data)
=> Task.FromResult(data.ToUpperInvariant());
}
string text = await new UpperCaseStrategy().ExecuteAsync("GoF");
There is also a concurrent ChainOfResponsibilityBase under DesignPatterns.Concurrent.Implementation.
Worked examples used by the test suite live under src/DesignPatterns.Tests/Examples.
Agent skill
Tool-agnostic instructions for applying these abstractions (pattern map, caveats, when not to use the package) live in SKILL.md. Point any coding assistant at that file when implementing or reviewing GoF usage with this library.
Resources
- Refactoring.Guru – Design Patterns
- Design Patterns: Elements of Reusable Object-Oriented Software (GoF)
Contributing
Issues and pull requests are welcome: improvements to existing abstractions, additional examples, or documentation fixes.
License
This project is licensed under the Apache License 2.0. See LICENSE.md for the full text.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
.NETStandard 2.1
- No dependencies.
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
2.0.2
* Added self-adapting Adapter abstractions: IAdapter<TResult>.Adapt() and DesignPatterns.Concurrent.IAdapter<TResult>.AdaptAsync(), for types that adapt themselves instead of a separate source object.
* Reviewed and corrected the XML documentation of every public interface and member, including malformed tags on the Factory Method and Builder abstractions, the previously undocumented IPrototype<TThis>.Clone(), missing type parameter and return descriptions, and inconsistencies between the synchronous and concurrent counterparts.
The package contains abstractions for the following GoF Design Patterns:
Creational patterns provide various object creation mechanisms, which increase flexibility and reuse of existing code.
* Abstract Factory
* Builder
* Prototype
* Factory Method
* Singleton
Structural patterns explain how to assemble objects and classes into larger structures while keeping these structures flexible and efficient.
* Adapter
* Bridge
* Composite
Behavioral design patterns are concerned with algorithms and the assignment of responsibilities between objects.
* Chain Of Responsibility
* Command
* Strategy
* Visitor