0% found this document useful (0 votes)
10 views16 pages

Design Patterns for c Student (1)

Design patterns are established solutions to common software design problems, enhancing code structure, maintainability, and clarity. The GoF catalog categorizes 23 foundational patterns into creational, structural, and behavioral types, while .NET employs Dependency Injection (DI) as a primary architectural pattern. Additionally, concepts like Inversion of Control (IoC) and code reflection are discussed, emphasizing their roles in modern software development.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views16 pages

Design Patterns for c Student (1)

Design patterns are established solutions to common software design problems, enhancing code structure, maintainability, and clarity. The GoF catalog categorizes 23 foundational patterns into creational, structural, and behavioral types, while .NET employs Dependency Injection (DI) as a primary architectural pattern. Additionally, concepts like Inversion of Control (IoC) and code reflection are discussed, emphasizing their roles in modern software development.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 16

Design Patterns for dotnet, struct and reflection

What is a design pattern & why it matters


Design patterns are reusable blueprints that experienced developers have tested over time to solve
common design issues in software. They help you structure your code in ways that are known to work
well in real-world applications.

They are a valuable tool for writing software that follows best practices. They are used by virtually every
programming language—in APIs, frameworks, and libraries—to promote consistency, maintainability,
and clarity.

By learning design patterns, developers can:

1. Leverage proven solutions, reducing the chance of introducing flawed or inefficient approaches.
2. Accelerate development, since you don’t have to reinvent effective solutions and can use a shared
vocabulary (for example, naming a solution “Factory Method” makes its intent immediately clear).

Not all problems require a pattern, and misuse can lead to over-engineering, however it’s valuable to
learn.

The GoF catalog (creational / structural / behavioral) at a glance


• GoF refers to Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, the four authors of
Design Patterns: Elements of Reusable Object-Oriented Software.
• This 1994 landmark book formally introduced the concept of software design patterns.
• It presents 23 foundational patterns, grouped into three categories:
o Creational (5) – how objects are instantiated (Singleton, Factory Method, Abstract Factory,
Builder, Prototype)
o Structural (7) – how classes and objects are composed into larger structures (Adapter, Bridge,
Composite, Decorator, Façade, Flyweight, Proxy)
o Behavioral (11) – how objects interact and distribute responsibility (Chain of Responsibility,
Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template
Method, Visitor)

Design Patterns in dotnet


Microsoft’s .NET Base Class Library (BCL) doesn’t just employ the classic GoF patterns—it’s also built
around Dependency Injection (DI) as its primary architectural pattern. By injecting services as
constructor parameters, .NET APIs (logging, configuration, hosting, etc.) let you depend on abstractions
rather than hard-coding concrete types—an application of Robert C. Martin’s Dependency Inversion
Principle (DIP) that Martin Fowler popularized. Many of those same GoF patterns then plug into your code
via DI. In short, before you dive into GoF patterns in .NET, it pays to master how DI and DIP work together.
Dependency Injection(DI):
Dependency
- Any object, usually instances that a class needs to do its work
- It is declared as a parameter in a class’ method usually in the constructor
- When you declare an object as a dependency, it is best practice to apply DIP(use abstractions). In
other words type your dependencies as an interface or abstract class rather than a specific
implementation

Injection
- the term “Injection” in DI is the term used when you passed an object (usually a class) to a method
when you invoke it.

Dependency Injection (DI) is just one way to implement the broader principle of Inversion of Control
(IoC).

What is Inversion of Control ( IoC)?


Inversion of Control (IoC) is a design principle where the responsibility for creating and managing objects is shifted away
from your code to an external system. Instead of manually instantiating dependencies, a framework or container supplies
them for you, making your application easier to organize, test, and extend. In simple terms, with IoC, your code focuses on its
main tasks while the system handles setting up the necessary supporting objects.

There are various ways to achieve Inversion of Control, including the Service Locator pattern and most commonly,
Dependency Injection (DI).

Today, IoC is considered a best practice, and nearly all major modern frameworks use it to some degree.
References:
• https://learn.microsoft.com/en-us/training/modules/configure-dependency-injection/2-understand-dependency-
injection
• https://learn.microsoft.com/en-us/training/modules/configure-dependency-injection/?source=recommendations
• https://www.baeldung.com/cs/ioc

Code Illustration if DI:

NOTE: I used a struct in the following example. The snippet contains the explanation of
what a struct is

//Usage
// injecting the ConsoleLogger
OrderService theOrderService = new OrderService(new ConsoleLogger());
theOrderService.PlaceOrder(new Order());

// Struct - just like a class but a class is a reference type a struct is a value type
// a value type is when you assign it to a variable, its value gets passed whereas
// a reference type is when you assign it to a variable; a reference to it gets passed;
// to illustrate:
// Order var1 = new Order("abc123", "Order 1");// Order is a struct; a value type
// Order var2 = var1; // var2 gets a copy of the object var1;
// changes you make to var2 will not affect var1 and vice versa
// var1.Name = "changed order";
// Console.WriteLine(var1.Name);
// now var2.Name is still "Order 1";
// Console.WriteLine(var2.Name);
// Reference type:
// ConsoleLogger var3 = new ConsoleLogger();// ConsoleLogger is a class; a reference type
// ConsoleLogger var4 = var3; // var2 gets a copy of the object var1;
// var3.ReferenceDemoField = "demo Field updated";
// Console.WriteLine(var3.ReferenceDemoField);
// changes you make to var3 will affect var4 and vice versa
// Console.WriteLine(var4.ReferenceDemoField);
public struct Order {
public string Id { get; }
// intentionally making this public
// so it can be modified from the outside for demo directly
public string Name { get; set; }

public Order()
{
Id = "order-0";
Name = "Order 0";
}

public Order(string id, string name) {


Id = id;
Name = name;
}
}

// Abstraction
public interface ILogger
{
void Log(string message);
}

// Concrete implementation
public class ConsoleLogger : ILogger
{
public string ReferenceDemoField= "What is my value?";
public void Log(string message) => Console.WriteLine(message);
}

// Class with a dependency on ILogger


public class OrderService
{
private readonly ILogger _logger;
// Dependency is injected via constructor
// DIP; declaring an abstraction as dependency type
public OrderService(ILogger logger)
{
_logger = logger;
}

public void PlaceOrder(Order order)


{
// use the dependency
_logger.Log($"Order ID: {order.Id}, Name: {order.Name} placed.");
}
}

Singleton design Pattern:


Ensures that there is only one instance of a class.

When you say a class is a singleton it means that class will only have one instance when the program
is running.

In .NET Core’s DI system, the services like ILogger<T> are registered as a singleton.

The Singleton is implemented by making class constructors as private and using lazy initialization.

Lazy initialization - the creation of an object is delayed until it is first used.

The singleton uses lazy initialization so that the object will only be created when GetInstance is called for
the first time.

Singleton sample implementation:


// Client usage
Logger logger = Logger.GetInstance();
logger.Log("Application started."); // Output: [Log]: Application started.
class Logger
{
private static Logger? instance;

// Private constructor
private Logger() { }
// Static method to return the instance
public static Logger GetInstance()
{
// this is what we call Lazy initialization
if (instance == null)
{
instance = new Logger();
}
return instance;
}

public void Log(string message)


{
Console.WriteLine($"[Log]: {message}");
}
}

https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging/src/LoggingSer
viceCollectionExtensions.cs

Illustration:
Using the ILogger
1. Create an MVC app.

Visual Studio:

VS Code:

dotnet new mvc -o PatternsDemo

2. Explore the files


a. Open Controllers/HomeController.cs
b. The following code shows how to inject dependencies in your code in .NET. The example uses
the ILogger built-in service of .NET:

private readonly ILogger<HomeController> _logger;

public HomeController(ILogger<HomeController> logger)


{
_logger = logger;
}
3. Using the logger to log that a user has visited the home page.
4. In ASP.NET Core (including MVC apps), the output of logs is controlled by the logging
providers(where the logs appear) you configure.
By default, when you create an MVC app using dotnet new mvc,
the WebApplication.CreateBuilder(args) (in the Program.cs) call automatically sets up
these logging providers: Console(dotnet run), Debug(Debug Console by Run->Start Debugging or
F5)

Add the following code inside Index:

_logger.LogInformation("User visited the Home page.");

public IActionResult Index()


{
// the user visited the Default page; log in the console that a user visited the home
page
_logger.LogInformation("User visited the Home page.");

return View();
}

Learn more at https://learn.microsoft.com/en-


us/aspnet/core/fundamentals/logging/?view=aspnetcore-9.0#logging-providers
5. Run the app

dotnet run

Visit the url used to launch the web app. This can be seen in the terminal.

then view the log in the terminal

OR
Run ->Start Debugging

This will launch a browser that loads the url for the app.

Creating your own singleton in c#


In .NET, when you have a class that you need to use in multiple places in your app (controllers, other
services, pages, etc.), you typically “register the class” as as a "service” in the app's Program.cs.

In the example, the PersonService will only be used in the Index method, so there is no need to put it in
the constructor like in the previous example. It can also be used in other parts of the code like in other
controllers.

1. Add the following files in your mvc app inside a new directory called “MyServices”.

IPerson.cs
namespace MyServices;

public interface IPerson


{
string GetName();
}
PersonService.cs
namespace MyServices;

public class PersonService: IPerson


{
public string GetName()
{
return "Luna Juan";
}
}
2. Register the Service by editing the Program.cs like so:

3. Edit the /Controllers/HomeController.cs


Include MyService namespace in the HomeController.cs
using MyServices;

Declare _person variable as IPerson;


private readonly IPerson _person;

Inject PersonService in the Constructor.


public HomeController(ILogger<HomeController> logger, IPerson person)
{
_logger = logger;
_person = person;

public IActionResult Index([FromServices] IPerson person)


{
// Log that the user visited the Home page
_logger.LogInformation("User visited the Home page.");

// Pass the person's name to the view using ViewData


ViewData["name"] = person.GetName();

// check if person and _person points to the same reference


ViewData["isSingleton"] = ReferenceEquals(person, _person);
// Reference for passing data to views:
// https://learn.microsoft.com/en-us/aspnet/core/mvc/views/overview?view=aspnetcore-
9.0#pass-data-to-views

// In ASP.NET Core MVC, calling View() without parameters


// returns a view file based on the convention:
// Views/{ControllerNameWithoutSuffix}/{ActionMethodName}.cshtml
// For this action (Index), it resolves to:
// Views/Home/Index.cshtml

// Reference for how controllers locate views:


// https://learn.microsoft.com/en-us/aspnet/core/mvc/views/overview?view=aspnetcore-
9.0#how-controllers-specify-views

return View();
}
4. Edit Views/Home/Index.cshtml

@{
ViewData["Title"] = "Home Page";
bool isSingleton = ViewData["isSingleton"] as bool? == true;

<div class="text-center">
<h1 class="display-4">Welcome @ViewData["name"]</h1>
<p>Person service @( isSingleton ? " IS a singleton." : " IS NOT a singleton.")</p>
<p>Learn about <a href="https://learn.microsoft.com/aspnet/core">building Web apps with
ASP.NET Core</a>.</p>
</div>

5. Run the app and view in the browser the changes.

Other sample at
https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/mvc/controllers/dependency-
injection/sample

Reference:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-
9.0

https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection

Introducing code reflection


Notice the following code in the example:
ILogger<HomeController>

ILogger is a generic type and we supplied the Controller Class as the type.

As we have tackled in the previous discussions, you use Generics to enable a class to accommodate
varying types of data in a type-safe way.

The ILogger needs the Type inside its implementations so it can determine which class triggered the log.

The ILogger uses code reflection to achieve this.

In the default implementation of ILogger<T>, the category name is typically set to the full name of the
type T (i.e., typeof(T).FullName). This process involves examining the type metadata at runtime to retrieve
its full name, which is a form of reflection.
Code Reflection
Reflection is a feature in a programming language that allows a running program to examine and interact
with its own structure and metadata at runtime. For example, a class can use Reflection to obtain the
names of its members, inspect their types, or even discover its own name.

See
https://github.com/dotnet/runtime/blob/main/src/libraries/Microsoft.Extensions.Logging.Abstractions/sr
c/LoggerT.cs#L50

Metadata is the structured “index” or “table of contents” that the C# compiler embeds in your assembly
(DLL, EXE, or package). It describes every component—types, methods, properties, fields, attributes,
references, and so on.
Reflection is the runtime API that reads that metadata, enabling frameworks and your own code to
discover and interact with those components.

What you see in IntelliSense (member lists, parameter info, Quick Info tooltips, etc.) is drawn directly
from the same metadata that reflection reads at runtime. At design time, Visual Studio (via the Roslyn
compiler APIs) loads each referenced assembly’s metadata tables—types, method signatures, property
definitions, XML doc comments, and attributes—and presents them in the editor without actually
executing your code.

The most used reflection API for programmers is for getting tthe type.

Once you get the Type of an object (via typeof(T) or myObj.GetType()), you can inspect or manipulate its
metadata — for example:

using System.Reflection;

Type typePerson = typeof(Demo);


Console.WriteLine("\n=== Members ===");
foreach (var m in typePerson.GetMembers())
Console.WriteLine(m.Name);

MethodInfo ?greet = typePerson.GetMethod("Greet", new Type[] { typeof(string) });


greet?.Invoke(new Demo(), new Object[] { "Mario" });

public class Demo


{
public void Greet(string name)
{
Console.WriteLine($"Hello {name}");
}
}

C# lets you define your own attributes to attach custom metadata to types (classes, interfaces etc.),
members, parameters etc.

Attributes in c# allow programmers to attach declarative information or metadata to code. You can
define custom attributes by defining classes that directly derives from the System.Attribute
class(https://learn.microsoft.com/en-us/dotnet/api/system.attribute?view=net-9.0). The custom
attribute can be applied just like any other built-in attribute.

// define type once; reflection


var typeDemo = typeof(Demo);
var typeDocuAtt = typeof(DocumentationAttribute);

// Reading a class' custom Metadata via reflection


var docAttr = (DocumentationAttribute?)
typeDemo.GetCustomAttribute<DocumentationAttribute>();
// OR typeDemo.GetCustomAttribute(typeDocuAtt);
Console.WriteLine($"Class docs: {docAttr?.Url}");

// Reading a method's custom Metadata via reflection


// with BindingFlags.Public | BindingFlags.Instance ; public and instance methods only(not
static)
var methodAtt = (DocumentationAttribute?) typeDemo.GetMethod("Greet", BindingFlags.Public |
BindingFlags.Instance)?
.GetCustomAttribute(typeDocuAtt);
Console.WriteLine($"Method docs: {methodAtt?.Url}");

// Listing a class' custom attributes reflection


// inherit: false means do not inherit from base types
Console.WriteLine("\n=== Custom Attributes ===");
foreach (var at in typeDemo.GetCustomAttributes<DocumentationAttribute>(inherit: false))
// OR foreach (var at in typeDemo.GetCustomAttributes(inherit: false))
Console.WriteLine(at.GetType().Name);

// Defining a custom attribute


[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DocumentationAttribute : Attribute
{
public string Url { get; }
public DocumentationAttribute(string url) => Url = url;
}

// Applying the custom attribute it


[Documentation("https://example.com/Demo")]
public class Demo
{
[Documentation("https://example.com/Demo/Greet")]
public void Greet(string name) { Console.WriteLine($"Hello {name}"); }
}

Learn more at:


• https://www.oracle.com/technical-resources/articles/java/javareflection.html
• https://learn.microsoft.com/en-us/dotnet/api/system.reflection?view=net-9.0
• Reflection in .NET | Microsoft Learn, https://learn.microsoft.com/en-
us/dotnet/fundamentals/reflection/reflection
• https://www.geeksforgeeks.org/what-is-reflection-in-c-sharp/
• https://learn.microsoft.com/en-us/dotnet/api/system.type?view=net-9.0#examples
• https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-
attributes/attribute-tutorial
• https://learn.microsoft.com/en-us/dotnet/api/system.reflection.bindingflags?view=net-9.0

Builder design pattern


The Builder is used when you are dealing with an object that is composed of many parts. This object can
have many representations (varying parts) and the object needs to be built in a series of steps(the object
cannot be built whole in one step). For example, an object that is configurable(configuration is composed
of many fields, configuration can vary, some object an have more or less, the value of the configuration
field varies). The configuration is applied to the object in a series of steps- where the configuration is
needed in the creation process. The object being created is referred to as the “product”.

The approach is:

• The product as a class that has flexible parts. It is not required that there must be a "common
required part" that is always present in all variations.
• There is one builder per product variation: each builder constructing a specific product variation.
The builder returns the object of type “product”.
• Have a base for all the builders to ensure structure (all required methods are present in all types)
that will serve as the type for all builders (used in DIP).
• Optionally there can a director that determines which builder to use based on a parameter.
.NET builder example:
.NET have several APIs that uses the builder pattern. The line of code below(found in Program.cs of an
mvc app) creates a builder. The builder it creates is the WebApplicationBuilder. It is the concrete builder,
that creates a WebApplication object through its build() method.

var builder = WebApplication.CreateBuilder(args); // returns a WebApplicationBuilder

the builder creates different variations of a WebApplication object depending on the calls made before its
.build() is called.

The other type of concrete builder is the HostApplicationBuilder. This is the builder for non web
applications.

These two builders both implement IHostApplicationBuilder interface. Their common parts are the
properties(properties in c# both functions as a data member and a function member – used as a data but
treated as a method by .NET Compiler):

• Properties
• Configuration
• Environment
• Logging
• Metrics
• Services
See code illustrations in the following:
• Builder design pattern code illustration, https://refactoring.guru/design-patterns/builder/csharp/example
• IHostApplicationBuilder source code,
https://github.com/dotnet/runtime/blob/0a33e18a0bccc10a0c4646dbf5b0fc70cbb3aa44/src/libraries/Microsoft.Ext
ensions.Hosting.Abstractions/src/IHostApplicationBuilder.cs
• HostApplicationBuilder source code,
https://github.com/dotnet/runtime/blob/0a33e18a0bccc10a0c4646dbf5b0fc70cbb3aa44/src/libraries/Microsoft.Ext
ensions.Hosting/src/HostApplicationBuilder.cs
• WebApplicationBuilder source code,
https://github.com/dotnet/aspnetcore/blob/main/src/DefaultBuilder/src/WebApplicationBuilder.cs

How do you create variety for WebApplicationBuilder?


Example 1

Instructing builder to customize logging providers for the WebApplication object that will be built.

NOTE: When trying out the example, make sure to clear the old logs first in the terminal and debug
window before you make changes and re-run the app.

var builder = WebApplication.CreateBuilder(args);


// Clear default providers; logging
builder.Logging.ClearProviders();
// Add the console logging provider
builder.Logging.AddConsole();
// uncomment the next line to enable logging in the debug console; NOTE you have to run the
app in DEBUGGING mode(VSiode Run->Start Debugging)
//builder.Logging.AddDebug();
// for logging to file see this example https://learn.microsoft.com/en-us/answers/questions/1377949/logging-in-c-
to-a-text-file
var app = builder.Build();
app.Run();

Example 2:

Configuring the WebApplication that will be created to include certain services.

See more complete example earlier “Creating your own singleton in c#”.

var builder = WebApplication.CreateBuilder(args);


// Register a custom service
builder.Services.AddSingleton<IMyService, MyService>();
var app = builder.Build();
app.Run();

In summary, you add the customizations via the exposed properties before the call to .Build().

Learn more at https://learn.microsoft.com/en-


us/dotnet/api/microsoft.aspnetcore.builder.webapplicationbuilder?view=aspnetcore-9.0

References:
• https://learn.microsoft.com/en-us/archive/msdn-magazine/2005/july/discovering-the-design-
patterns-you-re-already-using-in-net
• https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/common-design-patterns
• https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-
apis/webapplication?view=aspnetcore-9.0

You might also like