Simple question for all you pragmatic object-oriented fellas.
I have read many times to avoid classes like "Processor", and "xxxxHandler" in order to agree to OO standards: and I believe it's a good measure for understandability of the system code.
Let's assume we have a software that scans some file structure, let's say a bunch of specific CSV files. Let's say we have an independent module called CsvParser.
class CsvParser {
public string GetToken(int position) { .. }
public bool ReadLine() { .. }
}
class MyCsvFile {
public string FullPath { get; }
public void Scan() {
CsvParser csvp(FullPath);
while (csvp.ReadLine())
{
/* Parse the file that this class represents */
}
}
}
This will save having a "FileScanner" class, which is a -Processor- type class. Something that will collect say, a bunch of files from a directory, and scan each.
class MyFileScan {
public string[] Files { get; set; }
public void GetFiles() { this.Files = Directory.GetFiles(..); }
public void ScanFiles() {
foreach (string thisFilePath in Files)
{
CsvParser csvp(thisFilePath);
/* ... */
}
}
}
The OO approach dictates having the MyCsvFile class, and then a method representing the operation on the object.
Any thoughts? What do you programmers think.
I'd agree with your philospohy but if it was me I'd probably call the class CsvFile and have a Parse method in addition to the Scan one. In OO programming it's always desireable to make your classes represent "things" (nouns in English).
That aside if I was asked to maintain your code I'd grasp what a CsvParser class is likely to be doing whereas MyFileScan would send me into fits of rage and cause me to have to read the code to work it out.
I think what you're describing is that objects should take care of operations that only require themselves, which is in general a good rule to follow. There's nothing wrong with a "processor" class, as long as it "processes" a few different (but related) things. But if you have a class that only processes one thing (like a CSV parser only parses CSVs) then really there's no reason for the thing that the processor processes not to do the processing on itself.
However, there is a common reason for breaking this rule: usually you don't want to do things you don't have to do. For example, with your CSV class, if all you want is to find the row in the CSV where the first cell is "Bob" and get the third column in that row (which is, say, Bob's birth date) then you don't want to read in the entire file, parse it, and then search through the nice data structure you just created: it's inefficient, especially if your CSV has 100K lines and Bob's entry was on line 5.
You could redesign your CSV class to do small-scale operations on CSV's, like skipping to the next line and getting the first cell. But now you're implementing methods that you wouldn't really speak of a CSV having. CSV's don't read lines, they store them. They don't find cells, they just have them. Furthermore, if you want to do a large-scale operation such as reading in the entire CSV and sorting the lines by the first cell, you'll wish you had your old way of reading in the entire file, parsing it, and going over the whole data structure you created. You could do both in the same class, but now your class is really two classes for two different purposes. Your class has lost cohesion and any instance of the class you create is going to have twice as much baggage, while you're only likely to use half of it.
In this case, it makes sense to have a high-level abstraction of the CSV (for the large-scale operations) and a "processor" class for low-level operations. (The following is written in Java since I know that better than I know C#):
public class CSV
{
final private String filename;
private String[][] data;
private boolean loaded;
public CSV(String filename) { ... }
public boolean isLoaded() { ... }
public void load() { ... }
public void saveChanges() { ... }
public void insertRowAt(int rowIndex, String[] row) { ... }
public void sortRowsByColumn(int columnIndex) { ... }
...
}
public class CSVReader
{
/*
* This kind of thing is reasonably implemented as a subclassable singleton
* because it doesn't hold state but you might want to subclass it, perhaps with
* a processor class for another tabular file format.
*/
protected CSVReader();
protected static class SingletonHolder
{
final public static CSVReader instance = new CSVReader();
}
public static CSVReader getInstance()
{
return SingletonHolder.instance;
}
public String getCell(String filename, int row, int column) { ... }
public String searchRelative(String filename,
String searchValue,
int searchColumn,
int returnColumn)
{ ... }
...
}
A similar well-known example of this is SAX and DOM. SAX is the low-level, fine-grained access, while DOM is the high-level abstraction.
This is Problem Domain vs. Solution Domain design.
In order to solve a problem, we can design our class to model real life objects, that is program according to Problem Domain.
Another way of programming is design according to Solution Domain.
For instance, when we are designing a Flight booking system, for Flight management expert, they will describe the flight trip as "route", "time", "angle" (I cann't really recall the term). If we design according to these model, it is called design according to Problem Domain.
We can also design using coordinate system (x, y, z), because we feel that as a programmer, we can deal with these more efficiently. This is design for Solution Domain.
The problem with Solution domain is, in the world of project, one thing which is constant is - CHANGE! the requirements will always change! If the requirements are change, you have to redesign you program.
However, If you model you classes as real life object, you are less affected by the changes, because real-life objects seldom change.
"Processor", and "xxxxHandler" <-- this is design to solution domain.
You could take a look at Domain-Driven Design --- DDD for shorts.
Related
At my work, I'm trying to create more modular systems, as we tend to use similar mechanics in our games that have minor variances. To do this, I have been making use of interfaces, but have been getting stumped on certain problems, particularly ones relating to the addition of small features.
EXAMPLE:
Take for instance our evolution system. I have created the IEvolvable interface, which has a property for the evolution level and an Evolve() method.
public interface IEvolvable
{
int evolution { get; }
bool IncreaseEvolution(int numEvolutions);
}
I then have an implementation of this interface on a Character class, and based on some conditions via my Evolution handling class, I want to evolve my character.
public class EvolutionHandler
{
public IEvolvable evolvable;
public void TryEvolveCharacter
{
if(someCondition)
{
evolvable.IncreaseEvolution(1);
}
}
}
Then, later down the line, we say, we want the character to evolve based on level! Fantastic. We have an ILevellable interface which contains Level, xp, etc.
public interface ILevellable
{
int Level{ get; }
int MaxLevel{get;}
int XP {get;}
bool LevelUp(int numLevels);
}
We can use this data to control when evolution takes place based on the change in level. But here's my problem:
My evolve handler class interfaces with IEvolvable... not ILevellable... So what do I do?
I can have IEvolvable extend ILevellable or vice-versa... or I can create a new interface which extends IEvolvable and ILevellable. Now I also have to modify my evolve handler to accomodate for these changes.
But what happens if we don't want the evolve handler to take into consideration the Level anymore in our new game? Do use the old code? Was I supposed to extend my old code to include the Ilevellable interfacing?
public interface ILevelEvolver : ILevellable, IEvolvable
{
}
public class EvolutionHandler2
{
public ILevelEvolver levelEvolvable;
public void TryEvolveCharacter
{
if(levelEvolvable.Level > 10)
{
evolvable.IncreaseEvolution(1);
}
}
}
the key words are :
separate what varies from what stay the same
one of SOLID principles : open for extension closed for modification
finally in your case would use Strategy pattern :
public interface IEvilutionChecker{
bool AllowEvolution();
}
public class EvolutionCheckerA : IEvilutionChecker{
private ILevellable levelEvolvable;
public EvolutionCheckerA(ILevellable levelEvolvable){
this.levelEvolvable = levelEvolvable;
}
public bool AllowEvolution(){
return levelEvolvable.Level > 10;
}
}
public class EvolutionCheckerB : IEvilutionChecker{
private IEvolvable evolvable;
public EvolutionCheckerB(IEvolvable evolvable){
this.evolvable = evolvable;
}
public bool AllowEvolution(){
return someCondition;
}
}
public class EvolutionHandler2
{
public IEvolvable evolvable;
public IEvilutionChecker EvolutionChecker {get;set;};
public void TryEvolveCharacter
{
if(EvolutionChecker.AllowEvolution())
{
evolvable.IncreaseEvolution(1);
}
}
}
The interfaces should not extend each other. Keep them separated. Also you should keep concepts separated. By that, EvolutionHandler should only accept IEvolable.
In TryEvolveCharacter method, you can check if the property is a ILevelable.
Take a look at the code:
class EvolutionHandler
{
public IEvolable Evolable { get; set; }
public void TryEvolveCharacter()
{
if (Evolable is ILevelable levelable && levelable.Level > 10)
{
Evolable.IncreaseEvolution(1);
}
else if (someCondition)
{
Evolable.IncreaseEvolution(1);
}
}
}
so at the future, if a character extends ILevelable, that level will be considered, if not, someCondition take place.
Once you are running into these types of issues it becomes evident I think that OOP has limitations, or rather it makes some things too easy. That doesn't mean it should be scrapped entirely and something else adopted, there's a lot we can still use it for. What if rather than using the interface you make meaningful changes to directly you pass around a service interface that acts as an adapter to the internal interface.
public interface IEvolutionService {
TryEvolveCharacter(IEvolvable evolvable);
}
The concrete implementation can have something like
public void TryEvolveCharacter(IEvolvable evolvable){
if (evolvable.Level > 10) {
evolvable.IncreaseEvolution(1);
..Maybe do something new that the IEvolvable just exposed but without changing our consumed interface!
}
}
It does add code and things to make these but you have options there too, a single service can stand in for multiple interfaces, but then you are violating the Single Responsibility Principle in SOLID, and basically just making things more complex than they should in an effort at making it less complex.
You could make this a method on static class, although that interferes with testability, so I'd say refactoring and adding in a new service to handle things like service.TryEvolveCharacter(someIEvolvable). You'd still have to maintain the interface on your public facing service, but that could be more manageable than the raw interface with nothing abstracted in front of it.
I gave my answer to be as close to your question as possible, but to me it is still less than ideal. I would consider having immutable structs (which can have interfaces, and also stick to the L2 CPU cache) for the data and passing those to services (which would be pure functions, that is to say stateless, they only deal with what is passed in). If you are writing game code and performance is an issue then that's going to be very useful.
If you were only using games as a metaphor maybe less so :)
A helpful article on structs, L2, and performance
In many cases, having an interface that includes members which would be meaningful for some implementations but not others can be a better pattern than trying to use different interfaces for different combinations of functionality. As a simple example, if Java or .NET had included in their basic enumerable interface a function to report a count if available, along with one to indicate if and how the count would be performed, then a wrapper class that concatenates two enumerations could efficiently report how many elements were in the combined enumeration if the constituent enumerations supported a count function, and could also let clients know whether its count function would be efficient and/or cacheable.
Another pattern that can be useful is for an interface to include asXX function which a class may implement as either returning a reference to itself (if it supports XX functionality) or constructing a wrapper object of suitable type. If XX is a wrapper-class type, functionality may be added to the wrapper class without having to change the interface that includes the asXX member or implementations thereof.
I am writing an app that processes a bunch of ticker data from a page. The main class that I am working with is called Instrument, which is used to store all the relevant data pertaining to any instrument. The data is downloaded from a website, and parsed.
class Instrument
{
string Ticker {get; set;}
InstrumentType Type {get; set;}
DateTime LastUpdate {get; set;}
}
My issue is that I am not sure how to properly structure the classes that deal with the parsing of the data. Not only do I need to parse data to fill in many different fields (Tickers, InstrumentType, Timestamps etc.), but because the data is pulled from a variety of sources, there is no one standard pattern that will handle all of the parsing. There are even some parsing methods that need to make use of lower level parsing methods (situations where I regex parse the stock/type/timestamp from a string, and then need to individually parse the group matches).
My initial attempt was to create one big class ParsingHandler that contained a bunch of methods to deal with every particular parsing nuance, and add that as a field to the Instrument class, but I found that many times, as the project evolved, I was forced to either add methods, or add parameters to adapt the class for new unforeseen situations.
class ParsingHandler
{
string GetTicker(string haystack);
InstrumentType GetType(string haystack);
DateTime GetTimestamp(string haystack);
}
After trying to adapt a more interface-centric design methodology, I tried an alternate route and defined this interface:
interface IParser<outParam, inParam>
{
outParam Parse(inParam data);
}
And then using that interface I defined a bunch of parsing classes that deal with every particular parsing situation. For example:
class InstrumentTypeParser : IParser<InstrumentType, string>
{
InstrumentType Parse(string data);
}
class RegexMatchParser : IParser<Instrument, Match> where Instrument : class, new()
{
public RegexMatchParser(
IParser<string, string> tickerParser,
IParser<InstrumentType, string> instrumentParser,
IParser<DateTime, string> timestampParser)
{
// store into private fields
}
Instrument Parser(Match haystack)
{
var instrument = new Instrument();
//parse everything
return instrument;
}
}
This seems to work fine but I am now in a situation were it seems like I have a ton of implementations that I will need to pass into class constructors. It seems to be dangerously close to being incomprehensible. My thoughts on dealing with it are to now define enums and dictionaries that will house all the particular parsing implementations but I am worried that it is incorrect, or that I am heading down the wrong path in general with this fine-grained approach. Is my methodology too segmented? Would it be better to have one main parsing class with a ton of methods like I originally had? Are there alternative approaches for this particular type of situation?
I wouldn't agree with attempt to make the parser so general, as IParser<TOut, TIn>. I mean, something like InstrumentParser looks to be quite sufficient to deal with instruments.
Anyway, as you are parsing different things, like dates from Match objects and similar, then you can apply one interesting technique that deals with generic arguments. Namely, you probably want to have no generic arguments in cases when you know what you are parsing (like string to Instrument - why generics there?). In that case you can define special interfaces and/or classes with reduced generic arguments list:
interface IStringParser<T>: IParser<T, string> { }
You will probably parse data from strings anyway. In that case, you can provide a general-purpose class which parses from Match objects:
class RegexParser: IStringParser<T>
{
Regex regex;
IParser<T, Match> parser;
public RegexParser(Regex regex, IParser<T, Match> containedParser)
{
this.regex = regex;
this.parser = containedParser;
}
...
T Parse(string data)
{
return parser.Parse(regex.Match(data));
}
}
By repeatedly applying this technique, you can make your top-most consuming classes only depend on non-generic interfaces or interfaces with one generic member. Intermediate classes would wrap around more complicated (and more specific) implementations and it all becomes just a configuration issue.
The goal is always to go towards as simple consuming class as possible. Therefore, try to wrap specifics and hide them away from the consumer.
I'll try to be as explicit as possible, in case there is a better solution for my problem than answering my question.
I'm working in C#.
I have a report template that can include any number of 'features' turned on. A feature might be a table of information, a pie/bar chart, a list, etc. I am generating the report as a text file, or a PDF (possibly other options in the future).
So far I have an IFeature interface, and some feature types implementing it: ChartFeature, ListFeature, etc.
I read the list of features enabled from the database and pass each one to a method along with the data id and the method returns a populated IFeature of the proper type.
I also have an IReportWriter interface that TextReportWriter and PdfReportWriter implement. That interface has a method: AddFeature(IFeature).
The problem is that AddFeature in each writer ends up looking like:
public void AddFeature(IFeature)
{
InsertSectionBreakIfNeeded();
if(IFeature is TableFeature)
{
TableFeature tf = (TableFeature)feature;
streamWriter.WriteLine(tf.Title);
for(int row=0; row < tf.Data.First.Length; row++)
{
for(int column=0; i < tf.Data.Length; i++)
{
if(i != 0)
{
streamWriter.Write("|");
}
streamWriter.Write(feature.Data[column][row]);
}
}
}
else if(IFeature is ListFeature)
{
ListFeature lf = (ListFeature)feature;
streamWriter.Write(lf.Title + ": ");
bool first = true;
foreach(var v in lf.Data)
{
if(!first)
{
streamWriter.Write(", ");
}
else
{
first = false;
}
streamWriter.Write(v);
}
}
...
else
{
throw new NotImplementedException();
}
sectionBreakNeeded = true;
}
In the PDF writer the above would be modified to generate PDF table cells, text boxes, and so forth.
This feels ugly. I like it somewhat better as AddFeature(ListFeature){...}, AddFeature(ChartFeature) because at least then it's compile time checked, but in practice it just moves the problem so now outside if the IReportWriter I'm calling if(feature is ...).
Moving the display code into the feature just reverses the problem because it would need to know whether it should be writing plain text or a PDF.
Any suggestions, or am I best just using what I have and ignoring my feelings?
Edit:
Filled in some of the conditions to give people a better idea of what is happening. Don't worry too much about the exact code in those examples, I just wrote it off the top of my head.
The general case of your problem is called double-dispatch - you need to dispatch to a method based on the runtime type of two parameters, not just one (the "this" pointer).
One standard pattern to deal with this is called the Visitor pattern. It's description traces back to the original Design Patterns book, so there's lots of example and analysis of it out there.
The basic idea is that you have two general things - you have the Elements (which are the things that you're processing) and Visitors, which process over the Elements. You need to do dynamic dispatch over both of them - so the actual method called varies depending on both the concrete type of the element and of the visitor.
In C#, and kinda sorta following your example, you'd define an IFeatureVisitor interface like this:
public interface IFeatureVisitor {
void Visit(ChartFeature feature);
void Visit(ListFeature feature);
// ... etc one per type of feature
}
Then, in your IFeature interface, add an "Accept" method.
public interface IFeature {
public void Accept(IFeatureVisitor visitor);
}
Your feature implementations would implement the Accept method like so:
public class ChartFeature : IFeature {
public void Accept(IFeatureVisitor visitor) {
visitor.Visit(this);
}
}
And then your report writers would implement the IVisitor interface and do whatever it's supposed to do in each type.
To use this, it's look something like this:
var writer = new HtmlReportWriter();
foreach(IFeature feature in document) {
feature.Accept(writer);
}
writer.FinishUp();
The way this works is that the first virtual call to Accept resolves back to the concrete type of the feature. The call to the Visit method is NOT virtual - the call to visitor.Visit(this) calls the correct overload since at that point it knows the exact static type of the thing that's being visited. No casts and type safety is preserved.
This pattern is great when new visitor types get added. It's much more painful when the elements (features in your case) change - every time you add a new element, you need to update the IVisitor interface and all the implementations. So consider carefully.
As I mentioned, there's been almost 20 years since the book was published, so you can find lots of analysis and improvements on Visitor pattern out there. Helpfully this gives you enough of a start to continue your analysis.
I would structure this in a slightly different way:
I wod have a IReport object that composes all the features in the report. This object would have methods AddFeature(IFeature) and GenerateReport(IReportWriter)
I would then have IFeature implement WriteFeature(IReport, IReportWriter) and this way delegate how the Feature is actually processed to the Feature itself.
The way you've structured the code makes me think that there is no way to write a Feature in a format agnostic way that can be processed by any given writer, so let the object itself deal with the issue.
I suppose you're trying to draw something (i.e. output it to pdf or text or whatever ...).
My guess would be to go with something like this:
interface IReportWriter {
void AddFeature(IFeature feature);
// Some other method to generate the output.
IOutput Render();
// Drawing primitives that every report writer implements
void PrintChar(char c);
void DrawLine(Point begin, Point end);
...
}
// Default implementation for ReportWriters
abstract class AbstractReportWriter {
private IList<IFeature> features = new List<IFeature>();
...
public void AddFeature(IFeature feature) {
this.features.Add(feature);
}
public IOutput Render() {
foreach(var feature in this.features) {
feature.RenderOn(this);
}
}
// Leave the primitives abstract
public abstract void PrintChar(char c);
public abstract void DrawLine(Point begin, Point end);
}
And on the feature side:
interface IFeature {
void RenderOn(IReportWriter);
}
And here is an example implementation of ChartFeature:
public class ChartFeature : IFeature {
...
public void RenderOn(IReportWriter report) {
// Draw the chart based on the primitives.
report.DrawLine(..., ...);
...
}
}
I'd avoid Visitor for two reasons: 1) it's complicated and 2) it seems your IFeature and IReportWriter hierarchies are both open to extension. Visitor is only good if the visited Element hierarchy is stable. See #Will's comment in https://stackoverflow.com/a/32256469/1168342. Simple is also a good design.
Here's what your code looks like in a UML class diagram:
AddFeature seems to be an inconsistent name. What this method is doing is formatting output, so I'd name it appropriately.
If you follow the Replace conditional with polymorphism refactoring, you can add an IFeature.WriteOutput() method that each concrete Feature will implement. Then your call in IReportWriter looks like
public void AddFeature(IFeature feature)
{
InsertSectionBreakIfNeeded();
feature.WriteOutput();
sectionBreakNeeded = true;
}
In a sense, you've applied only the Strategy pattern to your code, where IFeature plays the role of Strategy and IReportWriter plays the role of Context:
Edit for Abstract Factory
It's looking less simple, but your example code didn't really consider all the permutations of [PDF, Text] and [Chart, List].
I suggest the Abstract classes/interfaces of PdfReportFeature and ListReportFeature in case there are some functions like creating a format preamble that would be there. You could possibly apply the Template Method pattern, if needed.
The idea is that each concrete class, e.g., PdfListFeature, will have its own WriteOutput method that does what it needs to do. The concrete ReportWriter just calls feature.WriteOutput() for whatever feature is injected (aggregated) into the report.
There's no double-dispatch since you won't be mixing PDF and Text reports together (Visitor really doesn't make sense to me). When you create a report, it's one or the other type. Your Abstract Factory pattern will help you create and pass the proper class for chart or list into the writer.
I updated the Strategy part above to be consistent with the Abstract Factory approach. I hope this makes sense.
Previous Post removed; Updated:
So I have a unique issue, which is possibly fairly common though. Properties are quite possibly are most commonly used code; as it requires our data to keep a constant value storage. So I thought how could I implement this; then I thought about how easy Generics can make life. Unfortunately we can't just use a Property in a Generic without some heavy legwork. So here was my solution / problem; as I'm not sure it is the best method- That is why I was seeking review from my peers.
Keep in mind the application will be massive; this is a very simple example.
Abstract:
Presentation Layer: The interface will have a series of fields; or even data to go across the wire through a web-service to our database.
// Interface:
public interface IHolder<T>
{
void objDetail(List<T> obj);
}
So my initial thought was an interface that will allow me to Generically handle each one of my objects.
// User Interface:
public class UI : IHolder
{
void objDetail(List<object> obj)
{
// Create an Instance
List<object> l = new List<object>();
// Add UI Fields:
l.Add(Guid.NewGuid());
l.Add(txtFirst.Text);
l.Add(txtLast.Text);
// l to our obj
obj = l;
return;
}
}
Now I have an interface; which has been used by our UI to put information in. Now; this is where the root of my curiosity has been thrown into the mixture.
// Create an Object Class
public class Customer : IHolder
{
// Member Variable:
private Guid _Id;
private String _First;
private String _Last;
public Guid Id
{
get { return _Id; }
set { _Id = value; }
}
public String First
{
get { return _First; }
set { _First = value; }
}
public String Last
{
get { return _Last; }
set { _Last = value; }
}
public virtual objDetail(List<Customer> obj)
{
// Enumerate through List; and assign to Properties.
}
}
Now this is where I thought it would be cool; if I could use Polymorphism to use the same interface; but Override it to do the method differently. So the Interface utilizes a Generic; with the ability to Morph to our given Object Class.
Now our Object Classes; can move toward our Entity interface which will handle basic Crud Operation.
I know this example isn't the best for my intention; as you really don't need to use Polymorphism. But, this is the overall idea / goal...
Interface to Store Presentation Layer UI Field Value
Implement the Properties to a Desired Class
Create a Wrapper Around my Class; which can be Polymorphed.
Morphed to a Generic for Crud Operation
Am I on the right path; is this taboo? Should I not do this? My application needs to hold each instance; but I need the flexibility to adapt very quickly without breaking every single instance in the process. That was how I thought I could solve the issue. Any thoughts? Suggestions? Am I missing a concept here? Or am I over-thinking? Did I miss the boat and implement my idea completely wrong? That is where I'm lost...
After pondering on this scenario a bit, I thought what would provide that flexibility while still ensuring the code is optimized for modification and business. I'm not sure this is the right solution, but it appears to work. Not only does it work, it works nicely. It appears to be fairly robust.
When is this approach useful? Well, when you intend to decouple your User Interface from your Logic. I'll gradually build each aspect so you can see the entire structure.
public interface IObjContainer<T>
{
void container(List<T> object);
}
This particular structure will be important. As it will store all of the desired content into it.
So to start you would create a Form with a series of Fields.
Personal Information
Address Information
Payment Information
Order Information
So as you can see all of these can be separate Database Tables, but belong to a similar Entity Model you are manipulating. This is quite common.
So a Segregation Of Concern will start to show slightly, the fields will be manipulated and passed through an Interface.
public interface IPersonalInformation
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
So essentially the Interface is passing its variable, to the Interface. So you would culminate an interface to handle that entire form or individual interfaces that you wish to call so that they remain reusable.
So now you have a series of Interfaces, or a single once. But it contains all these variables to use. So you would now create a class:
public class CustomerProperties: IPersonalInformation, IOrderInformation
{
// Implement each Interface Property
}
Now you've created a container that will hold all of your values. What is nifty about this container is you can reuse the same values for another class in your application or choose different ones. But it will logically separate the User Interface.
So essentially this is acting similar to a Repository.
Now you can take these values and perform the desired logic. What becomes wonderful now, is after you've performed your logic you pass the object into our Generic List. Then you simply implement that method in another class for your goal and iterate through your list.
The honesty is it appears to work well and decouple nicely. I feel that it was a lot of work to do something similar to a normal Repository and Unit Of Work, this answers the question but weather or not it is ideal for your project I would look into Repository, Unit Of Work, Segregation Of Concern, Inversion Of Control, and Dependency Injection. They may do this same approach cleaner.
Update:
I thought about it after I wrote this up, I noticed you could actually implement those property values into the Generic List structure bypassing a series of interfaces; but that would introduce consistency issues as you'd have to be aware of what data is being passed in each time, in order. It's possible, but may not be ideal.
Say I have a number of usercontrols, each usercontrol inside a tabitem, inside a window.
For example, let say this is a food collection application. Then we have tabs Fruit, Vegetables and Snacks. Each tab will show a list of food of that subject, and allow the user to add, delete, modify the food in each section. The food is stored in seperate textfiles, i.e. Fruit.txt, Vegetable.txt, Snack.txt
The actual text files might look something like this (vegetable.txt):
Name Carbs Fat
Eggplant 2 1.1
Cucumber 3 0.5
etc
Now this is a large list and there is a load method which pulls all the vegetables out into a List
The question I have is this loadVegetables method is in the code behind file, and I end up repeating this load method all over the place, because I have another of other screens like ReviewAllFood, AddVegetable, etc. along with all the other load methods for fruit and snacks.
This is more of a design question, I'm wondering how I set this up to not repeat this code. I could have a VegetableManager (or something) class where the load method is, but does this actually mean less repeated code? Then in each screen I have to create object of VegetableManager and call its load method anyway. So I guess efficiency wise its no better, but I do achieve a better design.
I think I'm missing something here. It's been a while since I studied cohesion and coupling and I think i'm confusing myself with these concepts at the moment. Appreciate if someone could suggest a design for this situation and explain why they chose it and why its better than how i'm doing it at the moment.
Thanks for reading.
I could have a VegetableManager (or
something) class where the load method
is, but does this actually mean less
repeated code? Then in each screen I
have to create object of
VegetableManager and call its load
method anyway.
The point of doing this is not efficiency (i.e. performance). The point is to encapsulate the details of loading that data into a single isolated object. Say for example that your site gets really big and you decide to move the data storage to a database for scalability and performance. In the existing code as you described, you'll have to go through each user control or page and change the logic of the load method. At the best this is a pain, and at the worst you miss some or copy-paste incorrectly. If the logic is encapsulated into a dedicated object, whose only responsibility is to know how to load the data from somewhere, then you only have to make the change once.
codebehind of user control:
protected void Page_Load(object sender, EventArgs e) {
var veggieManager = new VegetableManager();
VeggieListControl.DataSource = veggieManager.GetAll();
VeggieListControl.DataBind();
}
VegetableManager.cs:
public class VegetableManager {
private static Collection<Vegetable> _veggies;
private static object _veggieLock;
public ReadOnlyCollection<Vegetable> GetAll() {
if (_veggies == null) {
lock(_veggieLock) { //synchronize access to shared data
if (_veggies == null) { // double-checked lock
// logic to load the data into _veggies
}
}
}
return new ReadOnlyCollection(_veggies);
}
public void Add(Vegetable veggie) {
GetAll(); // call this to ensure that the data is loaded into _veggies
lock(_veggieLock) { //synchronize access to shared data
_veggies.Add(veggie);
// logic to write out the updated list of _veggies to the file
}
}
}
Because _veggies is static, there is only one collection of veggies in memory, despite the fact that multiple callers will instantiate VegetableManager. But because it's static, if you have a multi-threaded application (e.g. a website) you must synchronize access to that field across all threads (hence the locks).
This is the tip of the iceberg in terms of good object-orientation. I recommend perusing UncleBob's SOLID principles, and Domain-Driven Design (free e-book).
So, yes you are repeating something, but all you're repeating is a method call, and that is ok to repeat. DRY means to mitigate the duplication of "logical" code, i.e. decision-making and algorithms; simple method calls do not fall under this. However, if you want, you can consolidate logic into a base class do this, effectively isolating the user controls from having to know about VegetableManager, though I think this is object-orientation overkill, or OOO :-)
public abstract class FoodUserControl : UserControl {
protected List<Vegetable> GetVeggies() {
return new VegetableManager().GetAll();
}
}
Then your actual controls would derive from this instead of from UserControl.
Update
Eager-loading VegetableManager.cs:
public class VegetableManager {
private static Collection<Vegetable> _veggies;
private static object _veggieLock;
static VegetableManager() {
// logic to load veggies from file
}
public ReadOnlyCollection<Vegetable> GetAll() {
return new ReadOnlyCollection(_veggies);
}
public void Add(Vegetable veggie) {
lock(_veggieLock) { //synchronize access to shared data
_veggies.Add(veggie);
// logic to write out the updated list of _veggies to the file
}
}
}
Notice this eager-loading version doesn't have to do double-checked locking around the load code in the constructor. Also notice that the load code is in a static constructor, since this code initializes a static field (otherwise, you'd be reloading the data from the file on every construction into the same shared static field). Because veggies are eager-loaded, you don't need to load in GetAll or Add.
I would suggest pulling the vegetables (or whatever it is you're loading) out once when you read the file. Then you store them in some underlying data model. You can bind the list, and whatever other controls you need to, to the underlying data model. The data gets loaded once, but various views can display it.
EDIT: Adding code
List<T> loadObjects(File file, ILineConversionStrategy strategy) {
// read eaqch line of the file
// for each line
T object = strategy.readLine(line);
list.add(object);
return listOfObjects;
}
EDIT 2: Data model
class FoodModel {
List<Vegetable> getVegetables();
List<Fruit> getFruit();
// etc
}
I would use the repository pattern for this. As a start, create one class containing methods to retrieve the objects from each text file:
public class FoodRepository
{
public IList<Vegetable> GetVegetables() { ... }
public IList<Fruit> GetFruit() { ... }
// etc.
}
This class should be the only class in your application that is aware that foods are actually stored in text files.
Once you get that working you might want to consider caching frequently used data to improve performance.
public interface IEatable {}
class Vegitable : IEatable
{ string Name { get; set; } }
class Fruit : IEatable
{ string Name { get; set; } }
public interface IEatableManager
{
List<Vegitables> LoadEatables(string filePath);
}
public class VetabaleManager : IEatableManager
{
#region IEatableManagerMembers
public List<Vegitable> LoadVegs(string filePath)
{
throw new NotImplementedException();
}
#endregion
}
.
.
.
There are several things you need to consider for using a design like above
Dependency Injection pattern
Continuous Integration
DRY
and a must read:
How can I practice better
object-oriented programming?