I have some device classes like the following sample:
public class MoveSensor() {
public uint GetData() {
// Some logic here
}
}
public class TemperatureSensor {
public double GetData() {
// Some logic here
}
}
public class MultiSensorUnit() {
public MultiSensorData GetData() {
// Some logic here
}
}
public class MultiSensorData {
public int SomeSensor1Data { get; set; }
public byte SomeSensor2Data { get; set; }
public double SomeSensor3Data { get; set; }
}
Yet I have a class to periodically collect a data from these devices:
public class DataCollector() {
public void CollectData() {
// Here I want to collect a data from all devices
}
}
Looks like I should use an interface:
public interface IDataRecievable {
[This is a problem place] GetData();
}
But I can't do it because GetData() returns different types from different devices. I need a design pattern to make usage of devices in DataCollector more universal and generic.
With all of your sensors returning different kinds of data, you may consider move your data processing into each sensor implementation.
If you can do that, here is how I would implement it.
Declare an interface
public interface IDataRecievable<T>
{
T GetData();
void CollectData();
}
And concrete classes:
public class MoveSensor : IDataRecievable<uint>
{
public void CollectData()
{
//do collect logic here
}
public uint GetData()
{
//do get data
}
}
public class TemperatureSensor : IDataRecievable<double>
{
public void CollectData()
{
//do collect logic here
}
public double GetData()
{
//do get data
}
}
And data collector class
public class DataCollector
{
public void CollectData()
{
var typesToRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => type.GetInterfaces().Any(x => x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(IDataRecievable<>)));
foreach (var type in typesToRegister)
{
dynamic sensor = Activator.CreateInstance(type);
sensor.CollectData();
}
}
}
I get all classes implement IDataRecievable<>, create an instance and call the CollectData() method. You can always call GetData() isstead of CollectData() if you want.
You could encapsulate the functionality of the returned data into various implementations of the same interface. For example, if the data will be displayed, each sensor returns a different type that displays the data in a different way. For example:
public interface Sensor {
Data GetData();
}
public interface Data {
void Display();
}
public class IntData : Data {
public void Display() { ... }
}
public class DoubleData : Data {
public void Display() { ... }
}
public class MoveSensor : Sensor {
public IntData GetData() {
// ... return IntData ...
}
}
public class TemperatureSensor : Sensor {
public DoubleData GetData() {
// ... return DoubleData ...
}
}
Then some client can iterate through each sensor and display the data:
List<Sensor> sensors = // ...
foreach (Sensor sensor in sensors) {
sensor.Display();
}
This is not restricted to displaying the data, the Data interface can incorporate any type of functionality. For example, if the data needed to be stored into a database, you can pass some proxy to the database to a method and each Data implementation would know how to store itself in the database:
public class DatabaseProxy {
public void StoreInt(int value) { ... }
public void StoreDouble(double value) { ... }
}
public interface Data {
void StoreData(DatabaseProxy proxy);
}
public class IntData : Data {
private int _value;
public IntData(int value) {
_value = value;
}
public void StoreData(DatabaseProxy proxy) {
proxy.StoreInt(_value);
}
}
public class DoubleData : Data {
private double _value;
public DoubleData(double value) {
_value = value;
}
public void StoreData(DatabaseProxy proxy) {
proxy.StoreDouble(_value);
}
}
The idea is to shift the responsibility of using the returned data away from some external entity and hand it to the data itself. Ergo, the Data implementation is closest to the data it is storing and thus, it should be responsible for handling it. If there are too many things that need to be accomplished by Data, then more complex techniques can be used, like handlers or callbacks to separate the data from the handling of data.
It depends on what the results of each sensor are going to be. You have given examples of a uint and a double. I guess other sensors could in theory return strings or even complex compound objects.
The answer from a sensor is meaningless without knowledge of what that sensor measures, and that would obviously be known to the consumer of this data. Is your problem in the intermediate storage in DataCollector?
I can imagine Dictionary where you would want to hold an arbitrary number of sensors and their results. Can you live with the boxing/unboxing performance overheads? If there isn't a massive number of sensors then this should be negligible. If so, you can do something like:
using System;
using System.Collections.Generic;
using System.Linq;
public interface IDataRecievable
{
object GetData();
}
public class PiSensor : IDataRecievable
{
public object GetData() {
return (object)3.14m;
}
}
public class StringSensor : IDataRecievable
{
public object GetData() {
return (object)"Hello World";
}
}
public class DataCollector
{
private List<IDataRecievable> sensors;
private Dictionary<Type, object> sensorResults = new Dictionary<Type, object>();
public DataCollector(IEnumerable<IDataRecievable> sensorsToPoll)
{
this.sensors = sensorsToPoll.ToList();
}
public T GetResultFromSensor<T>(Type sensorType)
{
return (T)this.sensorResults[sensorType];
}
public void CollectData()
{
foreach (IDataRecievable sensor in this.sensors)
{
sensorResults[sensor.GetType()] = sensor.GetData();
}
}
}
public class Program
{
public static void Main()
{
List<IDataRecievable> sensors = new List<IDataRecievable>
{
new PiSensor(),
new StringSensor()
};
DataCollector dc = new DataCollector(sensors);
dc.CollectData();
decimal pi = dc.GetResultFromSensor<decimal>(typeof(PiSensor));
string greeting = dc.GetResultFromSensor<string>(typeof(StringSensor));
Console.WriteLine(2 * pi);
Console.WriteLine(greeting);
}
}
I would also probably go with Trung Le's implementation, but I would also have some interface for giving the result of the collecion:
public interface ICollectResultReceiver
{
void ReceiveCollectResult(
// whatever you are storing
object someData
);
}
I would also use another interface for the collection process, because it feels like data collection isn't a part of getting data:
public interface IDataRecievable<T>
{
T GetData();
}
public interface IDataCollectable
{
void CollectData(ICollectDataResultReceiver resultReceiver);
}
This way the user of IDataCollectable classes don't need to care about the types, and just use a for loop to run all the collection.
public interface IDataCollector
{
void Add(IDataCollectable collectable);
void CollectData();
}
public class DataCollector : IDataCollector
{
private readonly ICollectDataResultReceiver _resultReceiver;
private readonly List<IDataCollectable> _collectables = new List<IDataCollectable>();
public DataCollector(ICollectDataResultReceiver resultReceiver)
{
_resultReceiver = resultReceiver;
}
public void Add(IDataCollectable collectable)
{
_collectables.Add(collectable);
}
public void CollectData()
{
foreach(var collectable in _collectables)
{
collectable.CollectData(_resultReceiver);
}
}
}
Other than that, I would also make interfaces for each sensor, because there are times where knowing what kind of module you're using is necessary.
public interface IMoveSensorDataReceivable : IDataReceivable<uint> { }
public interface ITemperatureSensorDataReceivable : IDataReceivable<double> { }
public interface IMultiSensorDataReceivable : IDataReceivable<MultiSensorData> { }
So MultiSensorData would be more visible of what kind of data it has.
public class MultiSensorData
{
public uint GetSensorData1() => _moveSensor.GetData();
public double GetSensorData2() => _temperatureSensor.GetData();
private readonly IMoveSensorDataReceivable _moveSensor;
private readonly ITemperatureSensorDataReceivable _temperatureSensor;
public MultiSensorData(
IMoveSensorDataReceivable moveSensor,
ITemperatureSensorDataReceivable temperatureSensor)
{
_moveSensor = moveSensor;
_temperatureSensor = temperatureSensor;
}
}
It is also important to use interfaces instead of concrete classes for better testing. Even though MultiSensorData doesn't need much testing, your tests would be like so:
public class TestClass
{
[Fact]
public void MultiSensorDataTest()
{
var dummyTempSensor = new DummyTempSensor();
var dummyMoveSensor = new DummyMoveSensor();
var multiSensorData = new MultiSensorData(dummyMoveSensor, dummyTempSensor);
Assert.Equal(10, multiSensorData.GetSensorData1());
Assert.Equal(0.5, multiSensorData.GetSensorData2());
}
private class DummyTempSensor : ITemperatureSensorDataReceivable
{
public double GetData() => 0.5;
}
private class DummyMoveSensor : IMoveSensorDataReceivable
{
public uint GetData() => 10;
}
}
Related
I have the following example code:
namespace ConsoleApp3
{
public interface IPriceList
{
double GetPrice();
}
public class PriceList : IPriceList
{
public double GetPrice()
{
return 2.3;
}
}
public interface ISensorValue
{
double GetValue();
}
public class SensorValue : ISensorValue
{
public double GetValue()
{
return 20.5;
}
}
public abstract class Condition
{
public abstract bool IsTrue();
}
public class PriceCondition : Condition
{
private IPriceList _priceList;
public PriceCondition(IPriceList priceList)
{
_priceList = priceList;
}
public override bool IsTrue() { return _priceList.GetPrice() > 4.4; }
}
public class SensorCondition : Condition
{
private ISensorValue _sensorValue;
public SensorCondition(ISensorValue sensorValue)
{
_sensorValue = sensorValue;
}
public override bool IsTrue() { return _sensorValue.GetValue() > 20.4; }
}
public class ScheduleManager
{
private List<Condition> _conditions = new List<Condition>();
public ScheduleManager(IPriceList priceList, ISensorValue sensorValue)
{
Condition condition = new PriceCondition(priceList);
_conditions.Add(condition);
condition = new SensorCondition(sensorValue);
_conditions.Add(condition);
}
}
internal class Program
{
static void Main(string[] args)
{
PriceList priceList = new PriceList();
SensorValue sensorValue = new SensorValue();
ScheduleManager deviceScheduleManager = new ScheduleManager(priceList, sensorValue);
}
}
}
I would like to convert this to using dependency injection with the following changes:
I don't want to pass any dependencies to ScheduleManager (they are only used to be be passed down to PriceCondition and SensorCondition. All the subclasses of Condition may have different set of dependencies and the number of dependencies which then needs to be passed through the constructor of ScheduleManager would become large. The dependencies needed by each Condition subclass should somehow be injected at that level.
The code in the ScheduleManager class which creates the two different Condition objects is just a simplified example. In the real case, Condition objects will be created dynamically based on the content of a JSON file.
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.
Obviously using virtual and override is the normal situation, but does this telecoms'ish example count?
public class Pipe
{
// whole bunch of protected member variables such as bandwidth, latency, download limit
// etc,
public int GetCost()
{
// work out cost based on above
}
}
public class BigFatPipe : Pipe
{
public BigFatPipe()
{
// sets up the member variables one way
}
}
public class CheapestPossiblePipe: Pipe
{
public CheapestPossiblePipe()
{
// sets up the member variables another way
}
}
then you might call
PrintPrice(new BigFatPipe())
PrintPrice(new CheapestPossiblePipe())
public void PrintPrice(Pipe pipe)
{
int a = pipe.GetCost();
....
}
You'll get two different answers. This isn't the most useful example but does it count?
This post here has a useful discussion of what exactly polymorphism is.
I think most definitions do not explicitly state that an object must have virtual functions to be polymorphic - so yes, I think your example counts.
Constructor overloading is a recognized method to implement static polymorphism. While this isn't really constructor overloading, it's close. So yes, I'd call it polymorphism.
This pattern does work, but introducing a bunch of classes will confuse the user uselessly: they will wonder what the classes do differently.
A few factories methods will do the same job and will be easier to understand and maintain:
public class Pipe
{
// whole bunch of private member variables such as bandwidth, latency, download limit
// etc,
public int GetCost()
{
// work out cost based on above
}
public static Pipe MakeBigFatPipe()
{
var result = new Pipe();
// sets up the member variables one way
return result;
}
public static Pipe MakeCheapestPossiblePipe()
{
var result = new Pipe();
// sets up the member variables another way
return result;
}
}
If I were you I would use folowing approach:
public interface IGetCost
{
int GetCost();
}
public class Pipe : IGetCost
{
public int GetCost(){}
}
public class BigFatPipe : IGetCost
{
//aggregation
private readonly Pipe _pipe;
public BigFatPipe(Pipe pipe)
{
_pipe = pipe;
}
public int GetCost() { }
}
public class CheapestPossiblePipe : IGetCost
{
private readonly Pipe _pipe;
public CheapestPossiblePipe(Pipe pipe)
{
_pipe = pipe;
}
public int GetCost() { }
}
public static void PrintPrice(IGetCost obj)
{
int cost = obj.GetCost();
Console.WriteLine(cost);
}
static void Main(string[] args)
{
IGetCost p;
p = new Pipe();
PrintPrice(p);
p = new BigFatPipe();
PrintPrice(p);
p = new CheapestPossiblePipe();
PrintPrice(p);
}
I also need to say that there're two different things - polymorphism and overloading
polymorphism
public class foo
{
public virtual void foo1{/*....*/}
}
public class fooA : foo
{
public override void foo1{/*....*/}
}
public class fooB : foo
{
public new void foo1{/*....*/}
}
public class fooC : foo
{
//new is the default modifier
public void foo1{/*....*/}
}
overloading
public class foo{
public int foo1{/*....*/}
public int foo1(int a){/*....*/}
public int foo1(string a){/*....*/}
public int foo1(int a, string b){/*....*/}
}
I need to refactor the following class:
public interface IEmployee
{
int VacationWeeks { get; }
int YearsWithCompany { set; get; }
double Salary { set; get; }
}
public class Employee : IEmployee
{
private readonly int vacationWeeks;
public Employee(int vacationWeeks)
{
this.vacationWeeks = vacationWeeks;
}
public int VacationWeeks
{
get { return vacationWeeks; }
}
public int YearsWithCompany { set; get; }
public double Salary { set; get; }
}
I need to make sure that VacationWeeks depends only on YearsWithCompany, and I am loading the mapping from the database. So far I have come up with this:
public class EmployeeNew : IEmployee
{
private Dictionary<int,int> vacationWeeksTable;
public EmployeeNew(Dictionary<int, int> vacationWeeksTable)
{
this.vacationWeeksTable = vacationWeeksTable;
}
public int VacationWeeks
{
get { return vacationWeeksTable[YearsWithCompany]; }
}
public int YearsWithCompany { set; get; }
public double Salary { set; get; }
}
This class implements what I want, but it still has one vulnerability: different instances of EmployeeNew in the same collection may have been created with different instances of vacationWeeksTable.
All instances of EmployeeNew in the same collection must refer to the same vacationWeeksTable.
The application I am refactoring uses lots of List all over the system, and we need to be able to modify YearsWithCompany and Salary, yet to guarantee that only one vacationWeeksTable is used per List. These lists are iterated several times; its elements are modified in each iteration.
Here is my imperfect solution. Suggestions are welcome:
// this class does two things, which I do not like
public class EmployeeList : IEnumerable<IEmployee>, IEmployee
{
private Dictionary<int, int> vacationWeeksTable;
private List<EmployeeSpecificData> employees;
private int currentIndex;
private EmployeeSpecificData CurrentEmployee
{
get { return employees[currentIndex]; }
}
public IEnumerator<IEmployee> GetEnumerator()
{
for (currentIndex = 0; currentIndex < employees.Count; currentIndex++)
{
yield return this;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int VacationWeeks
{
get { return vacationWeeksTable[YearsWithCompany]; }
}
// this is ugly repetitive code I don't like
public int YearsWithCompany
{
get { return CurrentEmployee.YearsWithCompany; }
set { CurrentEmployee.YearsWithCompany = value; }
}
// this is ugly repetitive code I don't like
public double Salary
{
get { return CurrentEmployee.Salary; }
set { CurrentEmployee.Salary = value; }
}
}
I use the following to create and init some of the classes that need default and shared behaviour. Maybe if you can refactor it will help:
It is some form of the Factory and FlyWeight patterns combined (the flyweight part can be removed in your scenario), which in addition has a concept of class Type shared handlers.
I simplified and removed some stuff that you wont need but there is more to remove, I added comments.
Usage would be: (app init)
Dictionary<int,int> vacationWeeksTable = new Dictionary<int,int>();
// fill the table
Factory<Employee>.Init(vacationWeeksTable);
The whenever you create a Employee class:
// remove grouping in the factory class to remove this null
Employee em = Factory<Employee>.Create(null);
It takes only a WeakReference to the classes so you don't have to worry about GC.
Each employee will have the shared vacationWeeksTable setup on creation, without the possibility to change it after from outside if not using the factory class.
You could change the vacation table for all running instances of Employee at any moment in the runtime of the app with:
// this will call the method registered for SetInitialdata on all instances of Employee classes.
// again remove grouping to remove that null
Factory<Employee>.Call(EventHandlerTypes.SetInitialData, null, vacTable);
Sample implementation of Employee:
class Employee : IBaseClass
{
private Dictionary<int, int> vacationWeeksTable;
public virtual void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> register)
{
group = 0; // disable different groups
register(new Key<Employee, int>(0), group, EventHandlerTypes.SetInitialData, SetVacationWeeksTable);
}
public virtual void RegisterSharedData(Action<IKey, object> regData)
{
// remove this from factory and interface, you probably dont need it
// I have been using it as a FlyWeight data store for classes.
}
private void SetVacationWeeksTable(object sender, SharedEventArgs e)
{
vacationWeeksTable = e.GetData<Dictionary<int, int>>();
}
}
Code pattern Implementation:
IBaseClass : interface that each of my classes that are creatable through a factory implement
public enum EventHandlerTypes
{
SetInitialData // you can add additional shared handlers here and Factory<C>.Call - it.
}
public class SharedEventArgs : EventArgs
{
private object data;
public SharedEventArgs(object data)
{
this.data = data;
}
public T GetData<T>()
{
return (T)data;
}
}
public interface IBaseClass
{
void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> regEvent);
void RegisterSharedData(Action<IKey, object> regData);
}
Utility generic classes:
public interface IKey
{
Type GetKeyType();
V GetValue<V>();
}
public class Key<T, V> : IKey
{
public V ID { get; set; }
public Key(V id)
{
ID = id;
}
public Type GetKeyType()
{
return typeof(T);
}
public Tp GetValue<Tp>()
{
return (Tp)(object)ID;
}
}
public class Triple<T, V, Z>
{
public T First { get; set; }
public V Second { get; set; }
public Z Third { get; set; }
public Triple(T first, V second, Z third)
{
First = first;
Second = second;
Third = third;
}
}
Factory class with slight modification to handle your scenario:
public static class Factory<C> where C : IBaseClass, new()
{
private static object initialData;
private static Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>> handlers = new Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>>();
private static Dictionary<IKey, object> data = new Dictionary<IKey, object>();
static Factory()
{
C newClass = new C();
newClass.RegisterSharedData(registerSharedData);
}
public static void Init<IT>(IT initData)
{
initialData = initData;
}
public static Dt[] GetData<Dt>()
{
var dataList = from d in data where d.Key.GetKeyType() == typeof(Dt) select d.Value;
return dataList.Cast<Dt>().ToArray();
}
private static void registerSharedData(IKey key, object value)
{
data.Add(key, value);
}
public static C Create(int? group)
{
C newClass = new C();
newClass.RegisterSharedHandlers(group, registerSharedHandlers);
// this is a bit bad here since it will call it on all instances
// it would be better if you can call this from outside after creating all the classes
Factory<C>.Call(EventHandlerTypes.SetInitialData, null, initialData);
return newClass;
}
private static void registerSharedHandlers(IKey subscriber, int? group, EventHandlerTypes type, Action<object, SharedEventArgs> handler)
{
handlers.Add(subscriber, new Triple<EventHandlerTypes, int, WeakReference>(type, group ?? -1, new WeakReference(handler)));
}
public static void Call<N>(EventHandlerTypes type, int? group, N data)
{
Call<N>(null, type, group, data);
}
public static void Call<N>(object sender, EventHandlerTypes type, int? group, N data)
{
lock (handlers)
{
var invalid = from h in handlers where h.Value.Third.Target == null select h.Key;
// delete expired references
foreach (var inv in invalid.ToList()) handlers.Remove(inv);
var events = from h in handlers where h.Value.First == type && (!#group.HasValue || h.Value.Second == (int)#group) select h.Value.Third;
foreach (var ev in events.ToList())
{
// call the handler
((Action<object, SharedEventArgs>)ev.Target)(sender, arg);
}
}
}
}
Make a class which contains a Dictionary. Creating or getting instance of this new class will load the dictionary in a consistent way. Then your BOs can take an instance of the class, thus ensuring they're all using the same data (because the class containingthe list knows how to load itself with the proper set of data).
I have two data structure classes (this is a simplified version of my code)
Animal: Has one property “int Age”
Person: Has one property “DateTime Birthday”
What I am trying to accomplish is to compile “Uploading” (persisting to database), which is common across all different data structure classes.
So mainly my goal is to have a small Upload method that looks like
foreach (TypeName typeName in Enum.GetValues(typeof(TypeName)))
{
IDataPopulator populator =
new DataFactory().CreateDataPopulator(typeName);
populator.Populate(string.Empty);
}
But the problem is that, populator returns an object instances of different types, which I am having trying to encapsulate since they have no common properties.
(IDataPopulator.TResult Populate(string data) in the code below)
Is there a way to get around this? Or does Strategy pattern not fit for this kind of scenario?
Here is the code I’ve been working with
public class Program
{
public static void Main()
{
foreach (TypeName typeName in Enum.GetValues(typeof(TypeName)))
{
IDataPopulator populator = new DataFactory().CreateDataPopulator(typeName);
populator.Populate(string.Empty);
}
}
}
public enum TypeName { Person, Animal }
public class Person { public DateTime Birthday { get; set; } }
public class Animal { public int Age { get; set; } }
public interface IDataPopulator
{
TResult Populate(string data);
}
class AnimalDataPopulator : IDataPopulator
{
public Animal Populate(string data)
{
// create an instance of Animal using data
}
}
class PersonDataPopulator : IDataPopulator
{
public Person Populate(string data)
{
// create an instance of Person using data
}
}
public class DataFactory
{
public IDataPopulator CreateDataPopulator(TypeName typeName)
{
switch (typeName)
{
case TypeName.Person:
return new PersonDataPopulator();
case TypeName.Animal:
return new AnimalDataPopulator();
default:
throw new ArgumentOutOfRangeException("typeName");
}
}
}
public class UploadContext
{
private readonly IUploader _Uploader;
public UploadContext(IUploader uploader) { _Uploader = uploader; }
public void Upload() { _Uploader.Upload(); }
}
public interface IUploader
{
void Upload();
}
class PersonUploader : IUploader
{
private Person _Person;
public PersonUploader(Person person) { _Person = person; }
public void Upload()
{
Console.WriteLine("Uploading person...");
}
}
class AnimalUploader : IUploader
{
private Animal _Animal;
public AnimalUploader(Animal animal) { _Animal = animal; }
public void Upload()
{
Console.WriteLine("Uploading animal...");
}
}
I don't see how the Strategy pattern would fit here. The pattern is about a family of algorithms, e.g. line breaking strategies. You don't have strategies here, you have a single abstract method
TResult Populate(string data);
and several implementations of it.
I also don't understand the initial problem, maybe you want to queue several serialization operations? In that case the Command pattern is your friend.
I think what you are after is either serialization, if you are just storing blobs, or an ORM framework.