What is the Marker Design Pattern?

Göksu Deniz
6 min readJun 18, 2024

--

Image is created by ChatGPT AI

In the kingdom of Software, King Code needed a way to identify special subjects for a grand event. His advisor, Sir Interface, suggested giving these special subjects a “Marker Badge”. This badge didn’t change their abilities but simply marked them as special.

For example, Sir Serializable was given a Marker Badge because he could travel far and return unchanged. When it was time for the event, the guards only needed to look for subjects with the Marker Badge to know who could participate.

In a neighboring kingdom, Queen Dotnet faced a similar challenge. Her advisor, Sir Attribute, proposed using magical runes called “Attributes”. There runes not only marked the special subjects but also provided extra information about their abilities.

Both kingdoms thrived using these simple solutions. The Marker Badge and Attributes made it easy to identify special subjects without changing their nature, making the kingdoms more efficient and organized.

This simpler story captures the essence of the Marker Design Pattern and its application in both Java and .NET.

But for sure to clearly understand, let’s get in deep.

In .NET, the concept of the Marker Design Pattern can be applied using interfaces. However, .NET also heavily utilized attributes to achieve similar purposes.

Marker Interface in .NET

A marker interface in .NET is an empty interface that you implement to signify that a class has some specific property or should be treated in a particular way.

Here is the basic usage of marker interface:

using System;

public interface IMarker
{
// No methods or properties
}

public class ExampleClass : IMarker
{
public string Name { get; set; }

public ExampleClass(string name)
{
Name = name;
}
}

public class MarkerTest
{
public static void Main()
{
ExampleClass example = new ExampleClass("Test");
if (example is IMarker)
{
Console.WriteLine("ExampleClass implements IMarker interface.");
}
}
}

In this example, IMarker is a marker interface with no methods or properties. ExampleClass implements this interface to indicate that it possesses certain characteristics.

Using Attributes in .NET

Attributes in .NET provide a more flexible and powerful way to add metadata to classes, methods, properties, etc. They are often preferred over marker interfaces because they can contain additional information and more versatile.

using System;

[AttributeUsage(AttributeTargets.Class)]
public class MarkerAttribute : Attribute
{
// You can add properties to the attribute if needed
}

[Marker]
public class ExampleClass
{
public string Name { get; set; }

public ExampleClass(string name)
{
Name = name;
}
}

public class MarkerTest
{
public static void Main()
{
var type = typeof(ExampleClass);
if (Attribute.IsDefined(type, typeof(MarkerAttribute)))
{
Console.WriteLine("ExampleClass has the Marker attribute.");
}
}
}

In this example, MarkerAttribute is an attribute class, and the ExampleClass is marked with this attribute. At runtime, you can check if a class has a specific attribute using Attribute.IsDefined.

Comparison:

Marker Interface

  • Simpler and straightforward.
  • Indcates a binary (yes/no) property.
  • Less flexible since it cannot hold additional data.

Attributes:

  • More powerful and versatile.
  • Can carry additional metadata.
  • Better suited for complex scenarios.

Real World Scenarios

Here are a few real-world scenarios where the Marker Design Pattern can be effectively utilized in .NET:

Serialization with Custom Rules

In a system where certain classes need to be serialized and others do not, a marker interface can be used to identify which classes should be serialized.

public interface ICustomSerializable
{
// No methods or properties
}

public class CustomSerializer
{
public void Serialize(object obj)
{
if (obj is ICustomSerializable)
{
// Custom serialization logic
}
}
}

public class SerializableClass : ICustomSerializable
{
public string Data { get; set; }
}

Dependency Injection and Registration

In a dependency injection setup, you might want to register certain services that need special handling or lifetime management.

public interface ISingletonService
{
// No methods or properties
}

public class ServiceRegistrar
{
public void RegisterServices(IServiceCollection services, Assembly assembly)
{
var singletonServices = assembly.GetTypes()
.Where(t => typeof(ISingletonService)
.IsAssignableFrom(t) && t.IsClass);

foreach (var service in singletonServices)
{
services.AddSingleton(service);
}
}
}

public class MySingletonService : ISingletonService
{
// Implementation
}

MassTransit Implementation

Using marker interfaces to differentiate between events and commands in a messaging system like MassTransit.

public interface IEvent
{
// No methods or properties
}

public interface ICommand
{
// No methods or properties
}

public class UserCreatedEvent : IEvent
{
public string UserId { get; set; }
}

public class CreateUserCommand : ICommand
{
public string UserName { get; set; }
}

public class EventProcessor
{
public void ProcessMessage(object message)
{
if (message is IEvent)
{
// Handle event
}
else if (message is ICommand)
{
// Handle command
}
}
}

Serialization Sample From Newtonsoft.Json

Attributes can be used to control how properties are serialized, such as marking certain properties to be ignored during serialization.

using System;
using Newtonsoft.Json;

public class User
{
public string Name { get; set; }

[JsonIgnore]
public string Password { get; set; }
}

In this example, the JsonIgnore attribute from Newtonsoft.Json marks the Password property to be ignored during serialization.

Validation

Attributes can be used to enforce validation rules on properties of a class.

using System;
using System.ComponentModel.DataAnnotations;

public class User
{
[Required]
[StringLength(50)]
public string Name { get; set; }

[Range(18, 100)]
public int Age { get; set; }
}

public class Validator
{
public bool Validate(object obj, out List<ValidationResult> results)
{
var context = new ValidationContext(obj);
results = new List<ValidationResult>();
return Validator.TryValidateObject(obj, context, results, true);
}
}

Feature Toggle or Capability Check

Attributes can be used to enable or disable certain features based on runtime conditions.

using System;

[AttributeUsage(AttributeTargets.Class)]
public class FeatureEnabledAttribute : Attribute
{
public string FeatureName { get; }

public FeatureEnabledAttribute(string featureName)
{
FeatureName = featureName;
}
}

public class FeatureChecker
{
public bool IsFeatureEnabled(object obj)
{
var type = obj.GetType();
var attribute = (FeatureEnabledAttribute)Attribute.GetCustomAttribute(type, typeof(FeatureEnabledAttribute));
return attribute != null && IsFeatureActive(attribute.FeatureName);
}

private bool IsFeatureActive(string featureName)
{
// Logic to check if the feature is active
return true; // For example purposes, always return true
}
}

[FeatureEnabled("NewFeature")]
public class FeatureEnabledClass
{
// Implementation
}

These examples illustrate how attributes and interfaces can be used to add rich metadata to classes and methods, allowing for more flexible and detailed configuration and behavior at runtime.

Conclusion

The Marker Design Pattern, whether implemented using marker interfaces or attributes, provides a powerful mechanism for adding metadata to classes and methods without altering their fundamental behavior. This pattern is particularly useful in scenarios where you need to:

  1. Classify Objects: By using marker interfaces or attributes, you can classify objects into different categories, such as events and commands in a messaging system or serializable and non-serializable objects in data processing application.
  2. Enable Conditional Logic: The presence of a marker interface or attribute can trigger specific behavior in your code, such as special handling for logging, auditing or feature toggling.
  3. Simplify Frameworks and Libraries: Many frameworks, such as MassTransit or dependency injection containers, use marker interfaces or attributes to manage configuration and behavior, making it easier to extend and maintain the framework.

Marker Interfaces:

  • Pros: Simple to implement and use, clear indication of a class’s capabilities.
  • Cons: Limited to boolean (yes/no) properties, cannot carry additional data.

Attributes:

  • Pros: More flexible and powerful, can carry additional metadata, widely used in .NET for serialization, validation and dependency injection.
  • Cons: Slightly more complex to implement and use.

In .NET, while marker interfaces are useful for simple classification and type-checking, attributes are generally preferred for their flexibility and ability to carry rich metadata. They enable more detailed and nuanced control over how classes and methods behave, making them a valuable tool in modern software development.

By understanding and applying the Marker Design Pattern effectively, developers can create more modular, maintainable and expressive codebases that clearly communicate the roles and capabilities of different components.

Thanks for reading! If you found the article helpful, you can clap and follow. So you will notified of new articles.

# Reference

It was created with the help of ChatGPT AI.

--

--

Göksu Deniz
Göksu Deniz

Written by Göksu Deniz

Software Engineer, passionate about creating efficient solutions. Skilled in mentoring teams to deliver successful projects. Always exploring new tech trends.