I have a class with a lot of functions in it:
MyClass.cs Methods:
Login
Upload file
Upload Folder
Upload large file
Download
and many more.
the problem is that the class grows and grows, and it's starting to be not comfortable to write the whole methods in the same class.
In one hand I want to invoke the functions like:
var myclass=new MyClass();
myclass.SomeMethod();
but the price is to put all the classes in the same file.
I there any design pattern for this? or any other ideas would be appreciated
What about using dependency inversion?
You can then define your main class such as this
public class MyClass
{
public IUserManagement UserManager { get; set; }
public IFileManagement FileManager { get; set; }
public IFolderManagement FolderManager { get; set; }
public MyClass()
{
}
}
With these interfaces
public interface IUserManagement
{
void Login(string username, string password);
void Logout();
}
public interface IFileManagement
{
void UploadFile(string path);
void UploadLargeFile(string path);
void DownloadFile(string filename, string savePath);
}
public interface IFolderManagement
{
void DownloadFolder(string path);
void UploadFolder(string path);
}
The Bulk of your code then goes into classes which perform a specific task such as this.
public class MyFileManager : IFileManagement
{
public void DownloadFile(string filename, string savePath)
{
//Add code here
}
public void UploadFile(string path)
{
//Add code here
}
public void UploadLargeFile(string path)
{
//Add code here
}
}
public class MyUserManager : IUserManagement
{
public void Login(string username, string password)
{
//Add code here
}
public void Logout()
{
//Add code here
}
}
public class MyFoldermanager : IFolderManagement
{
public void DownloadFolder(string path)
{
//Add code here
}
public void UploadFolder(string path)
{
//Add code here
}
}
You can then create your class and call methods likes this
MyClass myClass = new MyClass()
{
FileManager = new MyFileManager(),
FolderManager = new MyFoldermanager(),
UserManager = new MyUserManager()
};
myClass.FileManager.DownloadFile("myfilename", #"c:\path\to\file.txt");
If you wanted, then you could also add wrappers in your MyClass to hide some of the complexity such as this.
//Add in MyClass
public void UploadFile(string path)
{
this.FileManager.UploadFile(path);
}
//Called like this
myClass.UploadFile(#"c:\path\to\other\file.txt");
This way, you can delegate responsibility for each functional area to a specific class which is easier to manage.
Just extract the code inside the methods of your MyClass to other classes. In MyClass you just call/delegate to the other classes.
This way you can reduce the amount of code in MyClass without reducing its method list.
Related
I have an existing C# console application that takes arguments and based on the arguments
creates an instance of markets (UK, US, MX..) using dependency injection.
Each market class does a 'string GetData()', 'string ProcessData()' and 'bool ExportData()'.
The application was initially created for one eCommerce vendor's markets. Now I am told to modify it for a different vendor that does a different process. The high-level flow remains the same.
'GetData' to fetch records from DB,
'ProcessData' for any transformation or the likes
'ExportData'.
The difference is Getdata() pulls records from DB and maps to an object. I am planning to use Petapoco. 'ProcessData' might return a similar class. 'Exportdata' currently does an API call but for the new vendor, I have to write to a file.
I was reading up on patterns I am totally confused. At first, I thought I needed abstract factory pattern and now I think the factory method is what I should be using but I am not sure if I am doing it right. Need some guidance/review here. A sample cs file I created from my understanding of factory pattern. This code is based on the headfirst code samples.
using System;
using System.Collections.Generic;
using StatusExport.Models;
namespace factorymethod
{
class Program
{
static void Main(string[] args)
{
ClientFactory factory = null;
Console.WriteLine("Enter client code:");
string clientCode= Console.ReadLine();
switch (clientCode.ToLower())
{
case "costco":
factory = new CostcoFactory("accountname", "taskname");
break;
//NEw vendor might be added
//case "walmart"
//factory = new WalmartFactory("taskname", "type");
//break
default:
break;
}
bool status = factory.ProcessData();
Console.ReadKey();
}
}
abstract class Client
{
public abstract string AccountName { get; }
public abstract string Task { get; set; }
//More properties might be added. Some may not even be used by some of the new vendors. For example, Costco Might need accountname and task. Tomorrow if walmart comes, they might not need these two or may need task and a new property 'type'
public abstract List<T> GetData<T>();
public abstract List<T> ProcessData<T>();
public abstract bool ExportData();
}
class CostcoClient : Client
{
public override string AccountName { get; }
public override string Task { get; set; }
public CostcoClient(string accountName, string task)
{
AccountName = accountName;
Task = task;
}
public override List<DBRecord> GetData<DBRecord>() //DBRecord class is specific to Costco.
{
List<DBRecord> dbresult = new List<DBRecord>();
//dbresult = db return data mapped to an object DBRecord using petapoco. Another vendor might have a different class to which DB records are mapped. So the return type can be generic
return asn;
}
public override List<T> ProcessData<T>()
{
throw new NotImplementedException(); //Any data transformation or business logic. Return type might be DBRecord or a new class altogether
}
public override bool ExportData()
{
throw new NotImplementedException();//Call API or write data to file and if success send true else false
}
}
abstract class ClientFactory
{
public abstract bool ProcessData();
}
class CostcoFactory : ClientFactory
{
public string AccountName { get; }
public string Task { get; set; }
public CostcoFactory(string accountname, string task)
{
AccountName = accountname;
Task = task;
}
public override bool ProcessData()
{
CostcoClient gc = new CostcoClient(AccountName, Task);
var result = gc.GetData<DBRecord>();
return true;
}
}
}
Do you think this is the right design approach?
I also want to keep the console project independent of vendor project. So maybe 'StatusExport.Program' for the console application. DLL projects StatusExport.Common to hold the interface and abstract classes' and 'StatusExport.Client(ex:StatusExport.Costco)' for each vendor stuff.
You can create BaseClient class that will contains a basic group of properties, and if you need to add something new - just inherit it. You did right, but i think it's better to change public modifier to protected in your properties AccountName and Task, to give access to them only from child classes.
Actually, you can create a BaseClientModels (request/response) for each method if you are not sure that returning type List will be always actual.
Example:
public abstract class BaseClient
{
#region Properties : Protected
protected abstract string AccountName { get; }
protected abstract string Task { get; set; }
#endregion
#region Methods : Public
public abstract BaseGetDataResponseModel GetData(BaseGetDataRequestModel model);
public abstract BaseProcessDataResponseModel ProcessData(BaseProcessDataRequestModel model);
public abstract BaseExportDataResponseModel ExportData(BaseExportDataRequestModel model);
#endregion
}
public class BaseGetDataResponseModel { }
public class BaseGetDataRequestModel { }
public class BaseProcessDataResponseModel { }
public class BaseProcessDataRequestModel { }
public class BaseExportDataResponseModel { }
public class BaseExportDataRequestModel { }
Then let's look on your class CostcoClient and how it can looks like:
public class CostcoClient : BaseClient
{
#region Properties : Protected
protected override string AccountName { get; }
protected override string Task { get; set; }
protected virtual IDataReader<BaseGetDataRequestModel, BaseGetDataResponseModel> DataReader { get; }
protected virtual IDataProcessor<CostcoClientProcessDataRequestModel, CostcoClientProcessDataResponseModel> DataProcessor { get; }
protected virtual IExportDataHandler<CostcoClientExportDataRequestModel, CostcoClientExportDataResponseModel> ExportDataHandler { get; }
#endregion
#region Constructors
public CostcoClient(string accountName, string task)
{
//set DataReader, DataProcessor, ExportDataHandler
AccountName = accountName;
Task = task;
}
#endregion
#region Methods : Public
public override BaseGetDataResponseModel GetData(BaseGetDataRequestModel model)
{
if (model is CostcoClientGetDataRequestModel clientGetDataRequestModel)
{
return DataReader.ReadData(clientGetDataRequestModel);
}
return null; //wrong type has passed
}
public override BaseProcessDataResponseModel ProcessData(BaseProcessDataRequestModel model)
{
if (model is CostcoClientProcessDataRequestModel clientProcessDataRequestModel)
{
return DataProcessor.ProcessData(clientProcessDataRequestModel);
}
return null;
}
public override BaseExportDataResponseModel ExportData(BaseExportDataRequestModel model)
{
if (model is CostcoClientExportDataRequestModel clientExportDataRequestModel)
{
return ExportDataHandler.Handle(clientExportDataRequestModel);
}
return null;
}
#endregion
}
public class CostcoClientGetDataRequestModel : BaseGetDataRequestModel { }
public class CostcoClientGetDataResponseModel : BaseGetDataResponseModel { }
public class CostcoClientProcessDataRequestModel : BaseProcessDataRequestModel { }
public class CostcoClientProcessDataResponseModel : BaseProcessDataResponseModel { }
public class CostcoClientExportDataRequestModel : BaseExportDataRequestModel { }
public class CostcoClientExportDataResponseModel : BaseExportDataResponseModel { }
public interface IDataReader<TIn, TOut>
{
public TOut ReadData(TIn model);
}
public interface IDataProcessor<TIn, TOut>
{
public TOut ProcessData(TIn model);
}
public interface IExportDataHandler<TIn, TOut>
{
public TOut Handle(TIn model);
}
public class CostcoClientDataReader : IDataReader<CostcoClientGetDataRequestModel, CostcoClientGetDataResponseModel>
{
public CostcoClientGetDataResponseModel ReadData(CostcoClientGetDataRequestModel model)
{
throw new NotImplementedException();
}
}
//and so on
You have to implement IDataReader, IDataProcessor, IExportDataHandler, make your logic and call it from GetData, ProcessData, ExportData methods, as an example, and get instances via dependency injection.
Then, we can change your factory to this:
public interface IClientFactory
{
BaseClient GetClientService(ClientServicesEnum value);
}
public class BaseClientFactory : IClientFactory
{
#region Propertied : Protected
protected virtual IEnumerable<BaseClient> Services { get; }
protected string AccountName { get; }
protected string Task { get; set; }
#endregion
#region Constructors
public BaseClientFactory(IEnumerable<BaseClient> services, string accountname, string task)
{
Services = services;
AccountName = accountname;
Task = task;
}
#endregion
public BaseClient GetClientService(ClientServicesEnum value)
=> Services.First(x => x.GetType().Equals(GetClientServiceByCode()[value]));
private Dictionary<ClientServicesEnum, Type> GetClientServiceByCode()
=> new Dictionary<ClientServicesEnum, Type>()
{
{ ClientServicesEnum.CostcoClient, typeof(CostcoClient) }
};
}
public enum ClientServicesEnum
{
CostcoClient = 1,
Another2 = 2,
Another3 = 3
}
Where
protected virtual IEnumerable<BaseClient> Services { get; }
you can get via DI too, and then get correct ServiceHandler by enum.
And your main function to call all this:
switch (clientCode)
{
case 1:
baseClient = ClientFactory.GetClientService(ClientServicesEnum.CostcoClient);
break;
case 2:
baseClient = ClientFactory.GetClientService(ClientServicesEnum.Another2);
break;
default:
break;
}
bool status = baseClient.ProcessData(null); //your model
The main thing is - you can use more than one pattern, for example one from Creational patterns, and one from Structural.
If i need some help in code architecture i use this:
https://refactoring.guru/
I think, using this example you can remove properties AccountName and Task, because of request models in methods.
In C# a static class can not derive from any other class besides object.
Currently I have this base class:
public static class BaseModule
{
public static string UsedSource {get; set;}
public static Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
Now, depending on which class I'm using, I want to change UsedSource.
// this does not work
internal static class ModuleA : BaseModule
{
static ModuleA(){
UsedSource = "A" // just an example
}
}
// this does not work
internal static class ModuleB : BaseModule
{
static ModuleB(){
UsedSource = "B" // just an example
}
}
Supposed to be called like this
ModuleA.Write("Hi");
ModuleB.Write("Hi");
This approach does not work because a static class cannot derive from anything else than object.
Is there any other way to change the property?
You have a lot of static classes going on here and I'm not entirely sure they're necessary. My example does not use static classes other than for the OtherStaticClass reference you have. I understand this may not be quite what you're looking for; many ways to skin this cat.
public abstract class BaseModule
{
public string UsedSource { get; set; }
public void Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
public class ModuleA : BaseModule
{
public ModuleA()
{
UsedSource = "A";
}
}
public class ModuleB : BaseModule
{
public ModuleB()
{
UsedSource = "B";
}
}
To get your output then, you just need to create new instances of ModuleA and ModuleB.
var moduleA = new ModuleA();
var moduleB = new ModuleB();
moduleA.Write("Hi");
moduleB.Write("Hi");
Using a static class means using a singleton. Singletons defeat the purpose of tracking the effective dependencies of your classes.
Anyway, you can approach the problem by refactoring your code and using a factory:
In this case, just drop the static keyword and let the class be inheritable (you have to add the appropriate virtual keywords to allow proper inheritance):
public class BaseModule
{
public string UsedSource {get; set;}
public Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
Then, add an additional class which holds the reference (I gave useless names, focus on the purpose):
public static class MySingleton
{
public static BaseModule _Module;
public static BaseModule Module
{
get
{
return _Module;
}
}
public static void ChangeImplementation (BaseModule module)
{
// do your checks here
_Module = module;
}
}
This way wou can achieve what you ask.
As you can see, this code has several issues, among them it's important to note that this code has global side effects and is not thread safe.
A better approach is to have drop the singleton entirely, and pass the BaseModule class (that can be inherited) as an argument of methods/constructors when needed.
I don't see that you need more than one static class. Instead separate the logic into methods in one static class.
public static class Module
{
private const string SourceA = "A";
private const string SourceB = "B";
public static WriteA(string text)
{
Write(SourceA, text);
}
public static WriteB(string text)
{
Write(SourceB, text);
}
private static Write(string source, string text)
{
OtherStaticClass.Log(source, text);
}
}
Then instead of
ModuleA.Write("Hi");
ModuleB.Write("Hi");
you'd do
Module.WriteA("Hi");
Module.WriteB("Hi");
If you can't change the BaseModule class, you can use it with other state and recover state after using:
public static class BaseModule
{
public static string UsedSource {get; set;}
public static Write(string text)
{
OtherStaticClass.Log(UsedSource, text);
}
}
internal class Writer : IDisposable
{
string _lastSource;
public Writer(string source)
{
_lastSource = BaseModule.UsedSource;
BaseModule.UsedSource = source;
}
public void Dispose()
{
BaseModule.UsedSource = _lastSource;
}
}
internal abstract class Module
{
public abstract Source { get; };
public void Write(string text)
{
using (var writer = new Writer(Source))
{
BaseModule.Write(text);
}
}
}
internal class ModuleA : Module
{
public override Source => "A";
}
internal class ModuleB : Module
{
public override Source => "B";
}
But you must ensure thread safety.
If you can change the BaseModule class:
public static class BaseModule
{
public static Write(string text, string source)
{
OtherStaticClass.Log(source, text);
}
}
internal abstract class Module
{
public abstract Source { get; };
public void Write(string text)
{
BaseModule.Write(text, Source);
}
}
internal class ModuleA : Module
{
public override Source => "A";
}
internal class ModuleB : Module
{
public override Source => "B";
}
I have 40 suppliers that need to make an ftp connection, do something there and close the connection. So, all of those 40 suppliers have their own class and they all have the connection and disconnection of the ftp server, but they all have different processing methods.
So basically I have 40 classes with this method:
ftp.Connect();
//do something - this is different for all the classes
ftp.Close();
So the do something part is different for all, it does different things, it uses different variables, etc.
What I thought I would do is: create a new class that would be instantiated in all the 40 suppliers. This class will have one method that look something like this:
public void Connect(FTPCredentials credentials, Process process)
{
var ftp = new FtpConnection(credentials.Host, credentials.Username, credentials.Password);
ftp.Open();
ftp.Login();
process(ftp);
ftp.Close();
}
public delegate void Process(FtpConnection ftp/*, string name*/);
The problem I have here is that all the methods in all 40 suppliers have different input parameters so what would the input parameters of Process be? Also, I think I don't gain much because I still have the FtpConnection ftp parameter here which means that I will have to add the dll that has the class FtpConnection in every project that will use the Connect method.
For example, the process method in the suppliers would look like this:
process(string fileName) //and it would download fileName
process(string folderName) //create folder if it doesnt exist
Is there a design pattern I can use here that would be cleaner and would make things easier?
My impression is that such an object is used only shortly and for a single specific purpose. So I would accept specific parameters to be stored in a specific derived class. Similar to mybirthname's solution, I'd start with an abstract class, but define it differently:
public abstract class BaseSupplier
{
protected BaseSupplier(FtpCredentials credentials)
{
_Credentials = credentials;
}
private FtpCredentials _Credentials;
public void Run()
{
Connect();
Process();
Disconnect();
}
private void Connect() {/* your connection and login code */}
private void Disconnect() {/* your disconnect code */}
protected abstract void Process(); // to be filled in the derived class
}
public class ConcreteSupplier
{
public ConcreteSupplier(FtpCredentials credentials, SomeType parameter) : base(credentials)
{ /* store extra parameters */ }
override Process() {/*your concrete processing code */ }
}
If I remember correctly, That's called the Strategy Pattern.
Edit:
juunas is right, it's the Template Method pattern. In Gamma et al., Template Method is described directly after Strategy in the chapter on Behavioral Patterns.
Create abstract class
public abstract class BaseSupplier
{
public void Connect(FTPCredentials credentials, Process process, SupplierSettingClass settings)
{
var ftp = new FtpConnection(credentials.Host, credentials.Username, credentials.Password);
ftp.Open();
ftp.Login();
DoSomething(settings);
ftp.Close();
}
public virtual void DoSomething(SupplierSettingClass settings)
{
//define base case;
}
}
You need to create SupplierSettingClass in which you will implement every input parameter for DoSomething method as property (folderName, fieldName and so on)
public class SupplierSettingClass
{
public string FolderName {get; set;}
//and so on;
}
In the end in the SupplierA
public class SupplierA:BaseSupplier
{
public override void DoSomething(SupplierSettingClass settings)
{
//Do specific stuff for your class.
}
}
You can use some clever inheritance to contain pretty much all of the required behaviour in one base abstract class, like this:
public interface IProcessor
{
void Process(Credentials credentials);
}
public class Credentials
{
public string Host { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
public abstract class SupplierBase : IProcessor, IDisposable
{
protected FtpConnection _Connection;
private void Connect(Credentials credentials)
{
//Create the ftp connection
_Connection = new FtpConnection(credentials.Host, credentials.Username, credentials.Password);
_Connection.Open();
_Connection.Login();
}
private void Disconnect()
{
//Close and dispose the ftp connection
_Connection.Close();
_Connection.Dispose();
_Connection = null;
}
public void Process(Credentials credentials)
{
Connect(credentials);
Execute();
Disconnect();
}
protected abstract void Execute();
#region IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_Connection != null)
{
_Connection.Dispose();
_Connection = null;
}
}
}
#endregion
}
public void MySupplier : SupplierBase
{
//You can add unique supplier properties here.
public string SomeProperty { get; set; }
protected override void Execute()
{
//Implementation here
Console.WriteLine(SomeProperty);
}
}
Here's an example of how you would call it:
Credentials creds = new Credentials()
{
Host = "127.0.0.1",
Username = "test",
Password = "test"
};
MySupplier sup1 = new MySupplier();
sup1.SomeProperty = "Hello";
sup1.Process(creds);
OR
using (MySupplier sup1 = new MySupplier())
{
sup1.SomeProperty = "Hello";
sup1.Process(creds);
}
Hey I have two classes
class Main
{
public exLog exLog;
public Main()
{
}
}
and
class exLog
{
public exLog()
{
}
public exLog(String where)
{
}
public exLog(String where, String message)
{
}
}
i tried to call exLog direct without giving exLog a parameter. So I can call any class with the Main Method.
How should I do that?
public String ReadFileString(String fileType, String fileSaveLocation)
{
try
{
return "";
}
catch (Exception)
{
newMain.exLog("", "");
return null;
}
}
I like to call them like a funtion in Main
You can call it as soon as you instantiate it.
public Main()
{
exLog = new exLog();
exLog.MethodInClass();
}
Also, if you are not in the same assembly you'll need to make exLog public.
Finally, this is C# and the style dictates that class names should be PascalCased. It's a good habit to form.
Methinks you want something like Adapter Pattern
class Main
{
private exLog exLog;
public Main()
{
}
public void ExLog()
{
exLog = new exLog();
}
public void ExLog(String where)
{
exLog = new exLog(where);
}
public void ExLog(String where, String message)
{
exLog = new exLog(where, message);
}
}
I think you're confused about classes, instances, constructors, and methods. This does not work:
newMain.exLog("", "");
because exLog in this case is a property, not a method. (It's confusing because you use the same name for the class and the property, which is why most conventions discourage that).
You can call a method on the instance:
newMain.exLog.Log("", "");
but then you'll need to change the names of the methods (and add a return type) in your exLog class so they don't get interpreted as constructors:
class exLog
{
public void Log()
{
}
public void Log(String where)
{
}
public void Log(String where, String message)
{
}
}
class Main
{
public exLog exLog;
public Main()
{
exLog = new exLog();
exLog.ReadFileString("", "");
}
}
So I have production code, compiled against production dlls. They access the real architecture of the system.
I'm working on a simulator that will simulate this architecture. All the classes in the simulation are named the same and belong to the same namespaces as the production classes.
People can use my simulation dlls to do a rough draft test of their code.
However, if they call existing production business logic that was compiled against the production classes, it will load the existing production dlls and use the real architecture.
If people want to use my simulation, but call existing business logic, I'd have to use some kind of injection to overwrite the dll that loads the production classes.
Is this possible?
For an example:
I have a dll called Production.dll
In it there is a class like so.
namespace Production
{
public class A { public void Do(); }
}
Now, I have a dll called Simulation.dll with the same class and code.
Someone wrote a program called DoA.exe
public static class Program
{
public static Main()
{
var a = new Production.A();
a.Do();
}
}
I want to make DoA.exe load my simulation dll, but I may not be able to remove Production.dll from its search path. How can I force it to use Simulation.dll instead.
I think I understand better your problem. While I think my original solution is cleaner, this is how to do it "dirty".
Assuming your class schema is like this (simplified):
// assembly: Production.dll (no dependencies)
namespace Production {
public class Test {
public void Do() {
Console.Out.WriteLine("Production");
}
}
}
// assembly: Simulation.dll (no dependencies)
namespace Production {
public class Test {
public void Do() {
Console.Out.WriteLine("Simulation");
}
}
}
// assembly: Usage.dll (dependency on Production.dll)
namespace Usage {
public class TestUsage {
public void Do() {
new Production.Test().Do();
}
}
}
And finally code that will perform the override:
// Console application ConsoleApplication.exe
// dependency on Production.dll, Usage.dll and Simulation.dll
namespace ConsoleApplication {
internal class AssemblyResolver : MarshalByRefObject {
static internal void Register(AppDomain domain) {
var resolver = domain.CreateInstanceFromAndUnwrap(
Assembly.GetExecutingAssembly().Location,
typeof(AssemblyResolver).FullName) as AssemblyResolver;
resolver.RegisterDomain(domain);
}
private void RegisterDomain(AppDomain domain) {
domain.AssemblyResolve += ResolveAssembly;
}
private Assembly ResolveAssembly(object sender, ResolveEventArgs args) {
var assemblyName = new AssemblyName(args.Name);
string name = assemblyName.Name;
// comment out line below and you'll load "Production" instead
if (name == "Production") {
name = "Simulation";
}
var fileNames = new[] { name + ".dll", name + ".exe" };
foreach (string fileName in fileNames) {
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);
if (File.Exists(path)) {
return Assembly.Load(File.ReadAllBytes(path));
}
}
return null;
}
}
class Program {
static void Main(string[] args) {
var domain = AppDomain.CreateDomain("Doable", null, new AppDomainSetup {
DisallowApplicationBaseProbing = true
});
AssemblyResolver.Register(domain);
domain.DoCallBack(() => {
// writes out "Simulation"
new Usage.TestUsage().Do();
});
}
}
}
I'm using Dependency Injection to handle problems like this:
Suppose you have interface like
public interface IDoable {
void Do();
}
in Interfaces.dll
and than you have Production.dll with class like:
namespace Production {
internal class Doable : IDoable {
public void Do() { Console.Out.WriteLine("Production"); }
}
public static class Bootstrapper {
static void Init(IServiceLocator locator) {
locator.AddSingleton<IDoable, Doable>();
}
}
}
and then you have Simulation.dll with class like
namespace Simulation {
internal class Doable : IDoable {
public void Do() { Console.Out.WriteLine("Simulation"); }
}
public static class Bootstrapper {
static void Init(IServiceLocator locator) {
locator.AddSingleton<IDoable, Doable>();
}
}
}
Then in your MainAssembly you can reference both, and resolve what implementation you want to use via configuration (naive example below). Except for the configuration line, you don't really have to care from which assembly the IDoable came from - you just use it.
public static Main()
{
Production.Bootstrapper.Init(ServiceLocator.Instance);
// or you can use
// Simulation.Bootstrapper.Init(ServiceLocator.Instance);
IDoable doable = ServiceLocator.Instance.Resolve<IDoable>();
doable.Do();
}
Utility classes (using Microsoft Unity Container from Enterprise Library):
public interface IServiceLocator {
void Add<TFrom, TTo>() where TTo : TFrom;
void BuildUp<T>(T instance);
void BuildUp(Type type, object instance);
void AddSingleton<TFrom, TTo>() where TTo : TFrom;
void AddSingleton<TFrom, TTo>(string name) where TTo : TFrom;
void AddSingleton(Type from, Type to, string name);
void AddInstance<T>(T instance);
T Resolve<T>();
T Resolve<T>(string name);
}
public class ServiceLocator : IServiceLocator {
private IUnityContainer m_Container = new UnityContainer();
public void Add<TFrom, TTo>() where TTo : TFrom {
m_Container.RegisterType<TFrom, TTo>();
}
public void BuildUp<T>(T instance) {
m_Container.BuildUp<T>(instance);
}
public void BuildUp(Type type, object instance) {
m_Container.BuildUp(type, instance);
}
public void AddSingleton<TFrom, TTo>() where TTo : TFrom {
m_Container.RegisterType<TFrom, TTo>(new ContainerControlledLifetimeManager());
}
public void AddSingleton<TFrom, TTo>(string name) where TTo : TFrom {
m_Container.RegisterType<TFrom, TTo>(name, new ContainerControlledLifetimeManager());
}
public void AddSingleton(Type from, Type to, string name) {
m_Container.RegisterType(from, to, name, new ContainerControlledLifetimeManager());
}
public void AddInstance<T>(T instance) {
m_Container.RegisterInstance<T>(instance);
}
public T Resolve<T>() {
return m_Container.Resolve<T>();
}
public T Resolve<T>(string name) {
return m_Container.Resolve<T>(name);
}
private static IServiceLocator s_Instance;
public static IServiceLocator Instance {
get { return s_Instance; }
}
static ServiceLocator() {
var instance = new ServiceLocator();
instance.AddInstance<IServiceLocator>(instance);
s_Instance = instance;
}
}