Since your organization can't seem to agree on naming things, it feels like you need to architect your application assuming there will be lots of things they don't agree on. It sounds like it will only get worse.
Maybe what you need instead is to adopt more of a facade or adapter pattern.
So, here is your "standard" person:
public class Person
{
public string Title { get; set; }
public string Forename { get; set; }
public string MiddleNames { get; set; }
public string Surname { get; set; }
// Enterprise wide behavior goes here
}
And the Billing Department can have its own way:
public class BillingDepartmentPerson
{
private readonly Person person;
public string GivenName
{
get { return person.Forename; }
set { person.Forename = value; }
}
public string FamilyName
{
get { return person.Surname; }
set { person.Surname = value; }
}
public string FirstName
{
get { return person.Forename; }
set { person.Forename = value; }
}
public string LastName
{
get { return person.Surname; }
set { person.Surname = value; }
}
public BillingDepartmentPerson(Person person)
{
this.person = person;
}
// Billing department specific behavior goes here
}
And so can the Shipping Department:
public class ShippingDepartmentPerson
{
private readonly Person person;
public string Title
{
get { return person.Title; }
set { person.Title = value; }
}
public string Forename
{
get { return person.Forename; }
set { person.Forename = value; }
}
public string MiddleNames
{
get { return person.MiddleNames; }
set { person.MiddleNames = value; }
}
public string Surname
{
get { return person.Surname; }
set { person.Surname = value; }
}
public ShippingDepartmentPerson(Person person)
{
this.person = person;
}
// Shipping department specific behavior goes here
}
Yes, there is some repetition of code, but now you have a way of encapsulating the "organization area specific" functionality and separate it from the common, enterprise wide functionality in the Person class.
It also means you can't accidentally invoke operations specific to the billing department in the context of the shipping department. It gives you a nice Separation of Concerns so refactoring these different areas can be isolated, and the effects of any refactoring jobs can be limited in scope (see also the Open/Closed Principal).
Paul commented:
if this had been VB.NET, it wouldn't have been a problem, I could have used an interface and aliased all the member names. But it isn't!
... in VB.NET I can do something like: Public Function x() As Boolean Implements IY.a. I haven't seen anything in C# to allow this.
C# does:
public class MyItems : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
throw new NotImplementedException();
}
// Implement an interface method explicitly
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
VB.NET and C# compile down to the same exact MSIL code that gets executed in the same exact Common Language Runtime.
C# and VB.NET, as long as you compare Visual Studio and .NET framework versions, support the same features.
Before answering your question, one should firstly know the goal developers are trying to achieve with the command pattern in the first place. More often than not, the pattern's purpose is to decouple modules from each other and to provide a sensible abstraction for execution of intents (commands) to the system.
In order to understand how commands fit into an application, let's integrate the command pattern by refactoring a hypothetical application which allows users to be registered. I apologise for the Java language, I haven't programmed in C# for a long time. In this very simple application we have a service and a controller:
class UserService {
private UserRepository userRepository;
private PasswordHashService passwordHashService;
public User registerUser(
String firstName,
String lastName,
String email,
String passwordInPlainText
) {
User userToBeRegistered = new User();
userToBeRegistered.setId(userRepository.retrieveNewId());
userToBeRegistered.setFirstName(firstName);
userToBeRegistered.setLastName(lastName);
userToBeRegistered.setEmail(email);
userToBeRegistered.setPassword(passwordHashService.hash(passwordInPlainText));
userRepository.save(userToBeRegistered);
return userToBeRegistered;
}
}
class UserController {
private UserService userService;
public Response<User> registerUser(FormInput formInput) {
return new Response<>(userService.registerUser(
formInput.getString("first_name"),
formInput.getString("last_name"),
formInput.getString("email"),
formInput.getString("password")
));
}
}
On the service layer, there's a design issue. The method takes 4 string arguments in a very specific order. This couples the caller of the method to the service and refactoring the method by adding a new optional argument might could end up being difficult if you called the registerUser
from multiple places.
In order to reduce the number of arguments of the service method, let's introduce a special DTO object, acting as a data messenger between the two layers, and for the same of convenience, name it RegisterUserCommand
. The Object would have the following structure:
class RegisterUserCommand {
private String firstName;
private String lastName;
private String email;
private String passwordInPlainText
// getters and setter are omitted
}
which will lead to design change of the service method, now looking like this:
public User registerUser(RegisterUserCommand command) {
User userToBeRegistered = new User();
userToBeRegistered.setId(userRepository.retrieveNewId());
userToBeRegistered.setFirstName(command.getFirstName());
userToBeRegistered.setLastName(command.getLastName());
userToBeRegistered.setEmail(command.getEmail());
userToBeRegistered.setPassword(passwordHashService.hash(
command.getPasswordInPlainText()
));
userRepository.save(userToBeRegistered);
return userToBeRegistered;
}
and the controllers method would change to:
public Response<User> registerUser(FormInput formInput) {
RegisterUserCommand command = new RegisterUserCommand();
command.setFirstName(formInput.getString("first_name"));
command.setLastName(formInput.getString("last_name"));
command.setEmail(formInput.getString("email"));
command.setPasswordInPlainText(formInput.getString("password"));
return new Response<>(userService.registerUser(command));
}
This fixes the unnecessary argument positional coupling, making it easier to introduce optional arguments to the user registration method by simply adding a new attribute to the command objects. In places where you don't work with the optional argument, no changes are necessary at all, in other places you may utilise the newly added property.
However the current design still contains coupling between the controller and the service. In this case, I wouldn't say it's a huge problem but since we're discussing a full command pattern integration, we'll refactor the code a bit further.
Introduction of a command bus
Using the command pattern without having some variant of a command bus is pretty meaningless. But what is this command bus? In short, it's mostly a glorified service locator. Some advanced implementations of command buses allow you to use plugins, middle-wares, effectively giving you the ability to extend the functionality of the command bus execution process without having to know it's internals.
Command busses generally consist of two main parts:
- service registry (some internal collection where instances of services live),
- command to service mapping (configuration telling which command type should be processed by which service).
Both of those two parts need to be configured by you. The 1. part requires you to somehow provide service instances to the command bus, and the 2. part requires you defining the mapping.
A very basic command bus implementation (really basic, no support for plugins etc.) could look like this:
interface Command {
}
interface CommandHandler<T extends Command> {
Object execute(T command);
}
class CommandBus {
private Map<Class<? extends CommandHandler<?>>, CommandHandler<? extends Command>> commandHandlers;
private Map<Class<? extends Command>, Class<? extends CommandHandler<?>>> commandToHandlerConfig;
public CommandBus() {
commandHandlers = new HashMap<>();
commandToHandlerConfig = new HashMap<>();
}
public void registerCommandHandler(CommandHandler<? extends Command> handler) {
Class<CommandHandler<?>> clazz = (Class<CommandHandler<?>>) handler.getClass();
if (commandHandlers.containsKey(clazz)) {
throw new RuntimeException("The command handler " + clazz + " is already registered.");
}
commandHandlers.put(clazz, handler);
}
public void registerCommandToCommandHandler(
Class<? extends Command> command,
Class<? extends CommandHandler<?>> handler
) {
if (!commandHandlers.containsKey(handler)) {
throw new RuntimeException("The command handler " + handler + " is not registered.");
}
commandToHandlerConfig.put(command, handler);
}
public <T extends Command, U> U dispatch(T command, Class<U> resultClass) {
Class<?> commandClass = command.getClass();
if (!commandToHandlerConfig.containsKey(commandClass)) {
throw new RuntimeException(
"The command " + commandClass + " could not be executed, no handler is configured."
);
}
Class<? extends CommandHandler<?>> handlerClass = commandToHandlerConfig.get(commandClass);
CommandHandler<? super Command> handler = (CommandHandler<? super Command>) commandHandlers.get(handlerClass);
return resultClass.cast(handler.execute(command));
}
}
This command bus allows you to register a command handler to a registry (fulfilling the 1. part) and to provide mapping for a given command to a certain handler (fulfilling the 2. part). Besides that it allows you to execute a command with an expected result class.
As a part of implementation, you may have noticed that that we've also introduced two interfaces, Command
and CommandHandler
. Those are necessary in compiled languages, but could be theoretically omitted in dynamic languages such as PHP or Python (as long as you'd follow certain guidelines defined by the command bus' implementation - mainly the method of the execution method of a handler).
Simple integration of our command bus
First we have to make our RegisterUserCommand
implement the Command interface
:
class RegisterUserCommand implements Command {
// [...] the rest remains the same
}
then we make the UserService
implement the CommandHandler
interface, i.e. we need to add an appropriate execute
method implementation. For the sake of simplicity, let's change our current registerUser
method to the execute
method.
class UserService implements CommandHandler<RegisterUserCommand> {
// [...] the rest remains the same
public Object execute(RegisterUserCommand command) {
// [...] the rest remains the same
}
}
These changes have been basically made so that the command and the service may be used in our command bus context. Right now you may have noticed a caveat. A class may only implement a generic interface only once, which forces you to basically have one command handler for each command. While you might find this troublesome at the beginning, in the long run this is actually very nice, since you'll end up with many tiny command handlers, each having only a single responsibility - handling a specific command instance.
A very basic integration of our command bus on the controller level could then look like the following (without actually making too much sense at the moment):
public Response<User> registerUser(FormInput formInput) {
RegisterUserCommand command = new RegisterUserCommand();
command.setFirstName(formInput.getString("first_name"));
command.setLastName(formInput.getString("last_name"));
command.setEmail(formInput.getString("email"));
command.setPasswordInPlainText(formInput.getString("password"));
CommandBus commandBus = new CommandBus();
commandBus.registerCommandHandler(userService);
commandBus.registerCommandToCommandHandler(
RegisterUserCommand.class,
userService.getClass()
);
return new Response<>(commandBus.dispatch(command, User.class));
}
Here we have manually created an instance of the command bus, registered a command handler and a command to handler mapping, basically forcing the command bus acting as a proxy. In reality, in big application there would usually be at most a few instances of a few separate command busses (or perhaps even only a single instance), which would come pre-configured, already containing all registered handlers and mappings, and such configured command bus would be injected, turning your controller into the following:
class UserController {
private CommandBus commandBus;
public Response<User> registerUser(FormInput formInput) {
RegisterUserCommand command = new RegisterUserCommand();
command.setFirstName(formInput.getString("first_name"));
command.setLastName(formInput.getString("last_name"));
command.setEmail(formInput.getString("email"));
command.setPasswordInPlainText(formInput.getString("password"));
return new Response<>(commandBus.dispatch(command, User.class));
}
}
Thanks to this, you are no longer coupled to the UserService
and the mapping is done on an infrastructure level (through configuration). This can be beneficial as it reduces number of dependencies of services, should a single service/controller/command handler invoke multiple commands.
The configuration could be e.g. a YAML, looking e.g. like this:
- CommandBus:
- Handlers:
- RegisterUser: UserService
- SomeOtherCommand: SomeOtherHandler
The CommandBus
would obviously need to provide a mechanism to correctly parse it.
Some advantages of a command bus
- Reduced coupling
- Seamless addition of plugins (when using a decent command bus implementation)
- Potential reduction of injected dependencies
- Leads to more cohesive and composable code (many small cohesive command handlers)
- Theoretically prepares project for different type of communication between layer, e.g. through an enterprise service bus
Some disadvantages of a command bus
- Complexity overhead (mapping through layers is done in configuration and is less visible)
- Each command has go through command bus proxy
- When going for a ready solution of a command bus, you lock yourself to the vendor
Other potential (dis)advantages may come up during discussion of command bus integration into your own project. Whether the approach to use a command bus is fitting for a given project or not depends on many variables and has to be talked through with the system's architect.
Best Answer
The Plain answer is that any kind of mapping or up-casting is bad as it risks a run time error when it could otherwise be prevented by strongly typed classes.
Edit: OP included app code.
The solution is Generics, which allow you to specify a Type that your library has no knowledge of.
This allows the app to specific the type of resource underneath the handler. painter can then be:
In Your case the type you need to use but the library doesn't know about is
OS.Graphics
.Because the library class itself has the method which needs this type
myLibrary.OnPaint
The library will need to be a generic type. The Generic type parameter propagates up to where this function is specified.Possibly you could refactor so that some sub class of library has the OnPaint method, then you could limit the generics to that sub type.