Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
Having the following problem. Article goes through several stages before being published by several different Roles.
Writer -> writes
Approver -> approves
Admin -> Publishes
Design a program that allow for maximum extensibility:
This is what I have. Can someone please let me know if this is the right approach? is there a better one?
public interface IRole
{
IRole Write();
IRole Approve();
IRole Publish();
}
internal class Writer : IRole
{
public IRole Write()
{
return new Approver(); // next needs approval
}
public IRole Approve()
{
return this;
}
public IRole Publish()
{
return this;
}
}
internal class Approver : IRole
{
public IRole Write()
{
return new Writer();
}
public IRole Approve()
{
// publish
return new Admin();
}
public IRole Publish()
{
return this;
}
}
internal class Admin : IRole
{
public IRole Write()
{
return new Writer();
}
public IRole Approve()
{
return new Approver();
}
public IRole Publish()
{
return this;
}
}
internal class Article
{
public Article()
{
role = new Writer();
}
private IRole role { get; set; }
public void Write()
{
role = role.Write();
}
public void Approve()
{
role = role.Approve();
}
public void Publish()
{
role = role.Publish();
}
}
private static void Main(string[] args)
{
var article = new Article();
article.Approve(); // can't do - write first
article.Write();
article.Publish(); // can't do
article.Approve(); // can
article.Write(); // can go back
article.Approve(); // can
article.Publish();
}
What you are looking for is called "Chain of Responsibility". It is a design pattern that allows operations to be chained one after the other. Your use case will perfectly fit with this pattern. https://www.codeproject.com/Articles/594974/Chain-of-Responsibility-Pattern
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.
This question already has answers here:
Interfaces — What's the point?
(32 answers)
Closed 5 years ago.
I have done demo implementation using interface:
class Program
{
static void Main(string[] args)
{
#region Calling method without interface
GreaterThanZeroTest objGreaterThanZeroTest = new GreaterThanZeroTest { SomeTestVariable = 10 };
Console.WriteLine(objGreaterThanZeroTest.SomeTestMethod().ToString());
LessThanZeroTest objLessThanZeroTest = new LessThanZeroTest { SomeTestVariable = -1 };
Console.WriteLine(objLessThanZeroTest.SomeTestMethod().ToString());
#endregion
#region Calling using interface
runTest(new GreaterThanZeroTest() { SomeTestVariable = 10 });
runTest(new LessThanZeroTest() { SomeTestVariable = 10 });
#endregion
Console.ReadKey();
}
public static bool runTest(ITest test)
{
return test.SomeTestMethod();
}
}
public interface ITest
{
int SomeTestVariable { get; set; }
bool SomeTestMethod();
}
// Determines whether an int is greater than zero
public class GreaterThanZeroTest : ITest
{
public int SomeTestVariable { get; set; }
public bool SomeTestMethod()
{
return SomeTestVariable > 0;
}
}
// Determines whether an int is less than zero
public class LessThanZeroTest : ITest
{
public int SomeTestVariable { get; set; }
public bool SomeTestMethod()
{
return SomeTestVariable < 0;
}
}
I see two benefit from the above implementation:
Code will look clean.
It will save one line of code.
What are the other benefits that we will get from such implementation and when we should consider interface in the application architecture?
IMHO, the most important thing for me is it allows you to
Mock some function for unit testing purpose.
It allows the implementation of "Inversion of Control" or IoC
There are plenty of reasons in the universe you can just search it though but these are what I think are important
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
Code below is for factory method pattern I would like it to be verified. if not valid then what are the changes that need to be made.
here i have added both client code from where the pattern is made use and the code for implementation of pattern.
the example i have used here is of TV remote which acts as factory and returns me TV channel object based on the channel number.
client code
private void button3_Click(object sender, EventArgs e)
{
ITVChannelNew channelNew;
channelNew = RemoteNew.getChannel(1);
currentProgram = channelNew.getCurrentShow();
channelNew = RemoteNew.getChannel(2);
currentProgram = channelNew.getCurrentShow();
}
Factory method code
namespace WindowsFormsApplication1
{
public interface ITVChannelNew
{
string getCurrentShow();
string getNextShow();
string getPreviousShow();
}
public class TVChannelNew : ITVChannelNew
{
private readonly string previous;
private readonly string current;
private readonly string next;
public TVChannelNew(string previous, string current, string next)
{
this.previous = previous;
this.current = current;
this.next = next;
}
public string getCurrentShow()
{
return current;
}
public string getNextShow()
{
return next;
}
public string getPreviousShow()
{
return previous;
}
}
public class BBCNew : TVChannelNew
{
public BBCNew():base("BBC previous","BBC current","BB next")
{
}
}
public class TimesNowNew : TVChannelNew
{
public TimesNowNew()
: base("TimesNow previous", "TimesNow current", "TimesNow next")
{
}
}
public static class RemoteNew
{
public static ITVChannelNew getChannel(int ChannelNumber)
{
switch (ChannelNumber)
{
case 1:
return new BBCNew();
case 2:
return new TimesNowNew();
default:
return new TimesNowNew();
}
}
}
}
Yes that looks good to me.
But for me I would want to have a clearer indication of what type I would expect on the client level. To do this I would define a enum somewhere such as:
public enum TVChannels
{
BBCNew,
TimesNowNew
}
And define the Factory like:
public static class RemoteNew
{
public static ITVChannelNew getChannel(TVChannels channel)
{
switch (channel)
{
case TVChannels.BBCNew:
break;
case TVChannels.TimesNowNew:
break;
default:
break;
}
}
}
This way you have clearer indication what type you want to return and also still leaves the client without knowing any of the concrete types, such as:
private void button3_Click(object sender, EventArgs e)
{
ITVChannelNew channelNew;
channelNew = RemoteNew.getChannel(TVChannels.BBCNew);
currentProgram = channelNew.getCurrentShow();
channelNew = RemoteNew.getChannel(TVChannels.TimesNowNew);
currentProgram = channelNew.getCurrentShow();
}
Also, if someone changed the numbers around in your factory (in the switch statement), it is not immediately clear that it is wrong. If he did spot something wrong he/she will have to ask 'what is channel 45 again?' or 'what is channel 3 meant to return?' and probably have to check some other source to check the correct values.
If you use the enumerations and someone swapped it around accidentally, the next person will stand a better chance in spotting a mistake due to the obvious wording.
Yes the factory looks fine too me, I would perhaps call the factory TVChannelNewFactory but otherwise it seems fine.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
static Eleve[] eleves = new Eleve[6];
static void Main(string[] args)
{
int[] notes;
eleves[0] = new Faineant("Schtroumpf", "Faineant");
eleves[1] = new Fourbe("Schtroumpf", "Fourbe");
eleves[2] = new Bon("Schtroumpf", "Bon");
eleves[3] = new Fayot("Schtroumpf", "Fayot");
eleves[4] = new PasTresBon("Schtroumpf", "PasTrèsBon");
eleves[5] = new PasTresBon("Schtroumpf", "PasTrèsBon 2");
eleves[1].triche(); //Here is the problem ! not swag.
}
*
class Personne
{ public virtual void triche() {}
public virtual void prepareLeCafe() { }
}
*
abstract class Eleve : Personne ;
*
class Tricheur : Eleve, ITricheur
{ void ITricheur.triche()
{
min = 15;
max = 15;
}
}
*
public interface ITricheur
{
void triche();
}
The method called when running "eleves[1].triche() ;" is the one in class Personne and not the one in class Tricheur : Eleve, ITricheur. Can someone explain us our error ? Thanks a lot !
In your case objects in Eleve[] must be of type Tricheur than it will take the implementation you want or all of your other classes (Faineant, Fourbe, ...) have also to implement the same implementation like Tricheur (but last is bad idea).
If Fourbe is a direct child of Eleve
The array you created is of type Eleve.
tatic Eleve[] eleves = new Eleve[6];
Eleve extends Personne and Personne contains a method triche. That is why the Personne.triche is getting called.
The class class Tricheur : Eleve, ITricheur is not actually comes after Eleve. So, there is no point for its triche to get called.
If Fourbe is a direct child of Tricheur
You need to override triche this way,
class Tricheur : Eleve, ITricheur
{
public void override ITricheur.triche()
{
min = 15;
max = 15;
}
}
Seems that you missed out something in your code example. I just added the missing parts from compiler and it works.
The question would now be: Do you like to get into Personne.triche() or into Tricheur.triche()?
internal class Program
{
private static Eleve[] eleves = new Eleve[6];
private static void Main(string[] args)
{
eleves[0] = new Tricheur("Schtroumpf", "Faineant");
eleves[1] = new Tricheur("Schtroumpf", "Fourbe");
eleves[2] = new Tricheur("Schtroumpf", "Bon");
eleves[3] = new Tricheur("Schtroumpf", "Fayot");
eleves[4] = new Tricheur("Schtroumpf", "PasTrèsBon");
eleves[5] = new Tricheur("Schtroumpf", "PasTrèsBon 2");
var eleve = eleves[1];
var tricheur = eleve as ITricheur;
if (tricheur != null)
tricheur.triche();
eleve.triche();
}
}
public interface ITricheur
{
void triche();
}
internal abstract class Eleve : Personne
{ }
internal class Personne
{
public virtual void prepareLeCafe()
{ }
public virtual void triche()
{ }
}
internal class Tricheur : Eleve, ITricheur
{
private int max;
private int min;
public Tricheur(string a, string b)
{
}
void ITricheur.triche()
{
min = 15;
max = 15;
}
}
Update
So to get into Tricheur.triche() you should change your implementation that way:
internal class Tricheur : Eleve, ITricheur
{
public Tricheur(string a, string b)
{
}
void ITricheur.triche()
{
// If you remove this method, you'll get
// always into the overriden method below.
}
public override void triche()
{
}
}
The two methods:
public virtual void triche() {}
and
void ITricheur.triche()
are two different methods. The letter one being an explicit implementation of ITricheur.triche.
In case you really want to create a virtual triche method in the ultimate ancestor, you have to override it in the descendant (regardless it's declared in the interface as well — the two will actually match), like this:
public override void triche() { … }
You have methods with same name at class and interface, by default you call method from Personne(parent) class. If you need call method from ITrecheur you must call
(eleves[1] as ITricheur).triche();
you issue is the following Tricheur inherits from Eleve so your debugger see the triche() as correct but at runtime will not do anything as virtual has no implentation and nothing will be executed
so you should do something like this
public class Personne {
public virtual void triche() { }
public virtual void prepareLeCafe() { }
}
public abstract class Eleve : Personne
{
public override void triche()
{
// here your code
}
}
Scenario
I'm building a system where each item gets reviewed by 2 different people. Whenever the first reviewer saves an item review it's then turn for the second checker to complete their individual review. If I submitted the first review and I open the item again then the item is put into a read only state because you can't review your own work. Also, the first reviewer can put the item into a pending state if more information is required, which the second review can't. Each user is given a specific reviewer roll whenever they select an item from a list.
What I have so far
Each time an item is loaded into the editor the person is given one of 2 roles, InitialReviewer or SecondReviewer.
public class Reviewer
{
public void AddReview(string review) { }
}
public class InitialReviewer : Reviewer, ICanPutIntoPendingState
{
public void PutIntoPendingState(string pendingState) { }
}
public class SecondReviewer : Reviewer
{
// Just use base class to add review
}
public class ReviewerService
{
private readonly Reviewer _reviewer;
public void AddReview(string review)
{
_reviewer.AddReview(review);
}
public void PutIntoPendingState(string pendingState)
{
_reviewer.PutIntoPendingState(pendingState);
}
}
A trimmed down version of my editor.
public class Editor
{
private readonly ReviewerService _reviewerService;
public Editor(ReviewerService reviewerService)
{
_reviewerService = reviewerService;
}
public void SaveCommand()
{
if(user chose a pending state && _reviewerService.Reviewer is ICanPutIntoPendingState) // Pending state is a dropdown.
_reviewerService.PutIntoPendingState("pending state");
else // the user made a complete review
_reviewerService.AddReview("user review");
}
}
Problem
The issue I'm having is that I can't seem to escape from having logic inside Save() from the Editor class that doesn't belong there.
Question
How do I rid myself of the logic inside of the Save() function from the Editor class? It appears to be violating the SRP principle. Checking whether the current reviewer object is of type ICanPutIntoPendingState is the big problem, I think.
Note that I've ommited all logic because there's quite a bit.
Woudn't be enough to give to ReviewerService one Save() method, which internaly call one Save() method of, at this point, abstract class Reviewer, whom abstact Save() method's concrete implementation is made in InitialReviewer and SecondReviewer. So you push decisional logic to classes with concrete implementation.
Hope this helps.
Perhaps you should consider moving business logic from Reviewer class to ReviewerService. Simple implementation would be then something like this:
public abstract class Reviewer
{
public abstract bool CanPutIntoPendingState { get; }
}
public class InitialReviewer : Reviewer
{
public override bool CanPutIntoPendingState
{
get
{
return true;
}
}
}
public class SecondReviewer : Reviewer
{
public override bool CanPutIntoPendingState
{
get
{
return false;
}
}
}
public class ReviewerService
{
private readonly Reviewer _reviewer;
public void AddReview(string review)
{
// do add review logic here
}
public void PutIntoPendingState(string pendingState)
{
if (_reviewer.CanPutIntoPendingState )
{
// do PutIntoPendingState logic here
}
}
}
public class Editor
{
private readonly ReviewerService _reviewerService;
public Editor(ReviewerService reviewerService)
{
_reviewerService = reviewerService;
}
public void SaveCommand()
{
if(user chose a pending state) // Pending state is a dropdown.
_reviewerService.PutIntoPendingState("pending state");
else // the user made a complete review
_reviewerService.AddReview("user review");
}
}
You can also consider to expose only one function on reviewer service, that takes input model. Service is then responsible to validate input and take appropriate action. Something like this:
public class ReviewerService
{
private readonly Reviewer _reviewer;
public void StoreReview(ReviewModel model)
{
// validate input here
// do business logic here
if (model.IsPendingState && _reviewer.CanPutIntoPendingState)
{
this.PutIntoPendingState("pending state");
}
else
{
this.AddReview(model.Review);
}
}
private void AddReview(string review)
{
// do add review logic here
}
private void PutIntoPendingState(string pendingState)
{
// do PutIntoPendingState logic here
}
}
public class ReviewModel
{
public string Review { get; set; }
public bool IsPendingState { get; set; }
}
public class Editor
{
private readonly ReviewerService _reviewerService;
public Editor(ReviewerService reviewerService)
{
_reviewerService = reviewerService;
}
public void SaveCommand()
{
ReviewModel model = new ReviewModel() {Review="user review",IsPendingState=user chose a pending state };
_reviewerService.StoreReview(model);
}
}