Created
June 22, 2020 14:01
-
-
Save carlosble/7ec255f680eec2d0a21e6f6c80e0da3b to your computer and use it in GitHub Desktop.
Decorator pattern and metaprogramming with C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Linq; | |
using System.Reflection; | |
using NUnit.Framework; | |
namespace Metaprogramming | |
{ | |
enum Role | |
{ | |
admin, | |
user, | |
anonymous | |
} | |
class User | |
{ | |
public Role Role { get; set; } | |
} | |
class Service | |
{ | |
public virtual void Execute(string someArgument, User user) | |
{ | |
Console.WriteLine("------- EXECUTE 1"); | |
// some logic | |
} | |
public virtual void Execute(string someArgument) | |
{ | |
Console.WriteLine("------- EXECUTE 2"); | |
// some logic | |
} | |
} | |
class AuthDecorator : Service | |
{ | |
private Service decorated; | |
public AuthDecorator(Service decorated) | |
{ | |
this.decorated = decorated; | |
} | |
public override void Execute(string someArgument) | |
{ | |
Console.WriteLine("AUTH"); | |
decorated.Execute(someArgument); | |
Console.WriteLine("END"); | |
} | |
public override void Execute(string someArgument, User user) | |
{ | |
if (user.Role == Role.admin) // cross-cutting concern - SRP | |
{ | |
Console.WriteLine("Checking Auth for role:" + user.Role); | |
decorated.Execute(someArgument, user); | |
Console.WriteLine("Final auth"); | |
} | |
else | |
{ | |
throw new UnauthorizedAccessException(); | |
} | |
} | |
} | |
class LoggingDecorator : Service | |
{ | |
private Service decorated; | |
public LoggingDecorator(Service decorated) | |
{ | |
this.decorated = decorated; | |
} | |
public override void Execute(string someArgument) | |
{ | |
Console.WriteLine("Logging call to Execute with:" + someArgument); | |
decorated.Execute(someArgument); | |
Console.WriteLine("Call finished"); | |
} | |
public override void Execute(string someArgument, User user) | |
{ | |
Console.WriteLine("Logging call to Execute with:" + someArgument); | |
decorated.Execute(someArgument, user); | |
Console.WriteLine("Call finished"); | |
} | |
} | |
class AnotherService | |
{ | |
[Logging] // AOP Aspect Oriented Programming | |
[WriteAction] | |
public virtual void Execute(String arg1) | |
{ | |
Console.WriteLine("--EXECUTE AnotherService"); | |
} | |
[Logging] | |
[Authorized] | |
[WriteAction] | |
public virtual void Execute(String arg1, User user) | |
{ | |
Console.WriteLine("--EXECUTE AnotherService with user"); | |
} | |
} | |
internal class WriteActionAttribute : Attribute | |
{ | |
} | |
class AnotherServiceProxy<T> | |
{ | |
private T target; | |
public AnotherServiceProxy(T target) | |
{ | |
this.target = target; | |
} | |
public void Execute(params Object[] args) | |
{ | |
var myType = target.GetType(); | |
var methods = myType.GetMethods(); | |
foreach (var method in methods) | |
{ | |
var parameters = method.GetParameters(); | |
if (args.Length == parameters.Length /* TODO check types */ && | |
method.IsPublic) | |
{ | |
var attributes = method.GetCustomAttributes(); | |
foreach (var attribute in attributes) | |
{ | |
if (attribute.GetType() == typeof(LoggingAttribute)) | |
{ | |
Console.WriteLine("LOGGING:" + args[0]); | |
} | |
if (attribute.GetType() == typeof(AuthorizedAttribute)) | |
{ | |
Console.WriteLine("CHECKING AUTH"); | |
if (args.Length == 2 && args[1].GetType() == typeof(User)) | |
{ | |
if (((User) args[1]).Role != Role.admin) | |
{ | |
throw new UnauthorizedAccessException(); | |
} | |
} | |
} | |
} | |
method.Invoke(target, args); | |
} | |
} | |
} | |
} | |
class AuthorizedAttribute : Attribute | |
{ | |
} | |
class LoggingAttribute : Attribute | |
{ | |
} | |
class Factory | |
{ | |
public static Service CreateService() | |
{ | |
return new AuthDecorator(new LoggingDecorator(new Service())); | |
} | |
public static AnotherServiceProxy<T> CreateAnotherServiceProxy<T>() where T : new() | |
{ | |
return new AnotherServiceProxy<T>(new T()); | |
} | |
} | |
public class Tests | |
{ | |
[Test] | |
public void TestLogging() | |
{ | |
var service = Factory.CreateService(); | |
service.Execute("hello"); | |
} | |
[Test] | |
public void TestAuth() | |
{ | |
var service = Factory.CreateService(); | |
service.Execute("Hello user", new User{Role = Role.admin}); | |
} | |
[Test] | |
public void TestAuthWithAttributes() | |
{ | |
var service = new AnotherService(); | |
service.Execute("Hello user"); | |
} | |
[Test] | |
public void TestAuthWithAttributesAndUser() | |
{ | |
var service = new AnotherService(); | |
service.Execute("Hello user", new User{Role = Role.anonymous}); | |
} | |
[Test] | |
public void TestAuthWithAttributesDecorator() | |
{ | |
var service = Factory.CreateAnotherServiceProxy<AnotherService>(); | |
service.Execute("Hello user", new User{Role = Role.admin}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment