473,320 Members | 1,848 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Design pattern for cost calculator app?

Hi,

I have a problem that I’ve tried to get help for before, but I wasn’t able to solve it then, so I’m trying to simplify the problem now to see if I can get some more concrete help with this because it is driving me crazy…

Basically, I have a working (more complex) version of this application, which is a project cost calculator. But because I am at the same time trying to learn to design my applications better, I would like some input on how I could improve this design. Basically the main thing I want is input on the conditionals that (here) appear repeated in two places. The suggestions I got before was to use the strategy pattern or factory pattern. I also know about the Martin Fowler book with the suggestion to Refactor conditional with polymorphism. I understand that principle in his simpler example. But how can I do either of these things here (if any would be suitable)? The way I see it, the calculation is dependent on a couple of conditions: 1. What kind of service is it, writing or analysis? 2. Is the project small, medium or large? (Please note that there may be other parameters as well, equally different, such as “are the products new or previously existing?” So such parameters should be possible to add, but I tried to keep the example simple with only two parameters to be able to get concrete help)

So refactoring with polymorphism would imply creating a number of subclasses, which I already have for the first condition (type of service), and should I really create more subclasses for the second condition as well (size)? What would that become, AnalysisSmall, AnalysisMedium, AnalysisLarge, WritingSmall, etc…??? No, I know that’s not good, I just don’t see how to work with that pattern anyway else?

I see the same problem basically for the suggestions of using the strategy pattern (and the factory pattern as I see it would just be a helper to achieve the polymorphism above). So please, if anyone has concrete suggestions as to how to design these classes the best way I would be really grateful! Please also consider whether I have chosen the objects correctly too, or if they need to be redesigned. (Responses like "you should consider the factory pattern" will obviously not be helpful... I've already been down that road and I'm stumped at precisely how in this case)

Regards,

Anders

The code (very simplified, don’t mind the fact that I’m using strings instead of enums, not using a config file for data etc, that will be done as necessary in the real application once I get the hang of these design problems):
Expand|Select|Wrap|Line Numbers
  1. public abstract class Service
  2. {
  3.     protected Dictionary<string, int> _hours;
  4.     protected const int SMALL = 2;
  5.     protected const int MEDIUM = 8;
  6.  
  7.     public int NumberOfProducts { get; set; }
  8.     public abstract int GetHours();
  9. }
  10.  
  11. public class Writing : Service
  12. {
  13.     public Writing(int numberOfProducts)
  14.     {
  15.         NumberOfProducts = numberOfProducts;
  16.         _hours = new Dictionary<string, int> { { "small", 125 }, { "medium", 100 }, { "large", 60 } };
  17.     }
  18.  
  19.     public override int GetHours()
  20.     {
  21.         if (NumberOfProducts <= SMALL)
  22.             return _hours["small"] * NumberOfProducts;
  23.         if (NumberOfProducts <= MEDIUM)
  24.             return (_hours["small"] * SMALL) + (_hours["medium"] * (NumberOfProducts - SMALL));
  25.         return (_hours["small"] * SMALL) + (_hours["medium"] * (MEDIUM - SMALL))
  26.             + (_hours["large"] * (NumberOfProducts - MEDIUM));
  27.     }
  28. }
  29.  
  30. public class Analysis : Service
  31. {
  32.     public Analysis(int numberOfProducts)
  33.     {
  34.         NumberOfProducts = numberOfProducts;
  35.         _hours = new Dictionary<string, int> { { "small", 56 }, { "medium", 104 }, { "large", 200 } };
  36.     }
  37.  
  38.     public override int GetHours()
  39.     {
  40.         if (NumberOfProducts <= SMALL)
  41.             return _hours["small"];
  42.         if (NumberOfProducts <= MEDIUM)
  43.             return _hours["medium"];
  44.         return _hours["large"];
  45.     }
  46. }
  47.  
  48. public partial class Form1 : Form
  49. {
  50.     public Form1()
  51.     {
  52.         InitializeComponent();
  53.         List<int> quantities = new List<int>();
  54.  
  55.         for (int i = 0; i < 100; i++)
  56.         {
  57.             quantities.Add(i);
  58.         }
  59.         comboBoxNumberOfProducts.DataSource = quantities;
  60.     }
  61.  
  62.     private void comboBoxNumberOfProducts_SelectedIndexChanged(object sender, EventArgs e)
  63.     {
  64.         Service writing = new Writing((int) comboBoxNumberOfProducts.SelectedItem);
  65.         Service analysis = new Analysis((int) comboBoxNumberOfProducts.SelectedItem);
  66.  
  67.         labelWriterHours.Text = writing.GetHours().ToString();
  68.         labelAnalysisHours.Text = analysis.GetHours().ToString();
  69.     }
  70. }
  71.  
May 5 '10 #1
7 11680
Anders,

I'm not sure I understand exactly what you are trying to do, but I think the Template Method design pattern may be what you're looking for. http://en.wikipedia.org/wiki/Template_method_pattern

You still use polymorphism, but it's done so in a more privately.

Provide an implemenation at Service for getHours. It would be something like the following:

public int getHours() {
if (NumberOfProducts <= SMALL) {
return getSmall();
}
else if (NumberOfProducts <= MEDIUM) {
return getMedium();
else return getLarge();
}

Service would also abstractly declare getSmall(), getMedium() and getLarge(), each of which returns an int.

The Writing and Analysis classes would each have to declare and define getSmall(), ...

Here are what both would look like.

For Writing, getSmall() would be:
int getSmall() {
return _hours["small"] * NumberOfProducts;
}

For Analysis, getSmall() would be:

int getSmall() {
return _hours["small"];
}

You'd provide similar implementations for getMedium() and getLarge() as well.

I'm not sure about public versus protected or private, but you can look that up in other template method examples on line.

The main idea is that you keep the conditional logic in the base class, but the events processed are pushed down to the derived classes.
May 5 '10 #2
@jhumelsine
Sorry about the formatting. That was my first post. There's a mistake in the getHours() method. The final } should be before the else, not after it.
May 5 '10 #3
Thanks for your reply! I have tried similar ideas, but unfortunately that doesn't quite accomplish what I want. Basically I want to avoid a lot of if-statements that would have to be changed all over the place if a parameter was added or changed. I want to achieve loose coupling so that if something changes I would only have to change in one place. In your example, what would happen e.g. if another size was added? Say XLarge or XSmall? And what if also more services were added? Then I would have to change the Analysis class to add the new sizes, as well as the Writing class and any other added service class...
I thought there would be a good object-oriented way of creating a simple class structure that would avoid complex if-statements and achieve loose coupling. Basically the idea is to have a calculator that calculates the hours needed for a project with different services provided (in this case Writing and Analysis, but there could be others, like say Administration), and the pricing depends on the number of products (manuals actually) produced. But analysis and writing depend on this number in different ways as you can see. Writing is priced progressively, so that if the number of products falls in the SMALL span it will have one price per manual, but if it goes into MEDIUM it will add the price for the whole SMALL span + the discounted MEDIUM price, and so on. While Analysis is much simpler, it just has a bulk price for each size category, period. If I were to restate the problem procedurally it would look like this:
Expand|Select|Wrap|Line Numbers
  1.  public class Conditional
  2.     {
  3.         private int _numberOfManuals;
  4.         private string _serviceType;
  5.         private const int SMALL = 2;
  6.         private const int MEDIUM = 8;
  7.  
  8.         public int GetHours()
  9.         {
  10.             if (_numberOfManuals <= SMALL)
  11.             {
  12.                 if (_serviceType == "writing")
  13.                     return 30 * _numberOfManuals;
  14.                 if (_serviceType == "analysis")
  15.                     return 10;
  16.             }
  17.             else if (_numberOfManuals <= MEDIUM)
  18.             {
  19.                 if (_serviceType == "writing")
  20.                     return (SMALL * 30) + (20 * _numberOfManuals - SMALL);
  21.                 if (_serviceType == "analysis")
  22.                     return 20;
  23.             }
  24.             else
  25.             {
  26.                 if (_serviceType == "writing")
  27.                     return (SMALL * 30) + (20 * (MEDIUM - SMALL)) + (10 * _numberOfManuals - MEDIUM);
  28.                 if (_serviceType == "analysis")
  29.                     return 30;
  30.             }
  31.             return 0;
  32.         }
  33.     }
Of course, I would never write it like that, but it may help to see it like that to see what I'm trying to refactor out into a good object-oriented design. One that avoids the conditional pretty much altogether. I would like to achive a structure where I could, as you say, have it only in one place, such as a base class or a factory. But I just can't wrap my head around it. I've looked at Fowler's examples for Replace conditional with polymorphism (and strategy). But his examples concern a simple switch statement. Here there are more dimensions to the conditional, and I can't get a grip on it...

Any help greatly appreciated!

Regards,

Anders
May 7 '10 #4
Here's another possibility of thinking about this problem. Instead of using template method, which depends upon inheritence, let's try chain-of-responsibility (CoR), which is based upon delegation, and does so by forming a chain of delegates.

Chain of Responsibility can be found at: http://en.wikipedia.org/wiki/Chain-o...bility_pattern

Rather than trying to type in all of the code, you can go there or look else where, but I can kind of describe it.

Decorator is comprised of a linked list of objects. Each either performs a task or it delegates the request to the next object in the link. This continues until an object performs the task or you reach the end of the chain.

if you think about this chain, it functions as a sequence of if/else if.../else commands, like you've shown. Each object would be a conditional check, and the last object would be the final defaulted else condition.

The difference is that it's not hard coded in any one method. The conditional logic is spread over several objects. Here are the relative advantages and disadvantages.

Advantages:
  • It's flexible. In your example, you'd have 3 main objects. One for small, one for medium and the default at the end. When XSmall and and XLarge are needed, these would be 2 new objects and they would be added at the beginning and near the end end of the chain. No other code would be affected. You would still need to implement the XSmall and XLarge CoR objects.
  • You could have several sets of chains for different kinds of customers or analysis. For example, Writing and Analysis could each have their own separate chain. One might use XSmall and XLarge and the other might not.
  • The chain can be updated dynamically. You may want to start with Small, Medium and Large, and then XSmall and XLarge can be added without having to stop your program!

Disadvantages:
  • It's not obvious what's going on. The logic depends upon the configuration of the links. You can't just "see" it like you can with traditional implemenations.
  • Someone has to configure the links. This won't be the user. It may not even be you the developer. However, it might be. It all depends upon where you want that control.
  • You have to be concerned about infinite loops. If a chain links back upon itself by accident, you could get stuck in an infinite loop.

One final note about XSmall and XLarge. As suggested above, if they are added, you'll need to implement them and compile and/or link them in. However, if you use the Prototype Pattern, you might be able to clone new objects without having to know their type. http://en.wikipedia.org/wiki/Prototype_pattern

By the way, Prototype is a misleading name. It should have been called Clone or Breeder.
May 7 '10 #5
rahuljat
1 Bit
The problem is driving me crazy, so I have tried before to get some assistance; however, I was unable to do so, so now that I have simplified it, I am hoping that I can receive some more concrete assistance...
Aug 24 '21 #6
gillsgarryy
1 Bit
I have the same issue. Any tips for solution?
Aug 26 '21 #7
Miash
2 2Bits
You have 6 different formulas for calculating ‘hours’, they look similar but it’s difficult to generalize them (seems it will introduce more complexity than simplification). 6 formulas means you have 6 implicit functions. So you need a pattern that allows easily implement variability of the function used. That is “strategy” pattern is ideal for you, initial proposal was good enough to look for ‘better’ solutions.
Sep 13 '21 #8

Sign in to post your reply or Sign up for a free account.

Similar topics

3
by: Omer van Kloeten | last post by:
The Top Level Design: The class Base is a factory class with a twist. It uses the Assembly/Type classes to extract all types that inherit from it and add them to the list of types that inherit...
2
by: Tim Smith | last post by:
Dear All, Silly question, but I am having trouble understanding the diagram on the inside back cover entitled "Design Pattern Relationships." It shows the relationships between all of the...
11
by: FluffyCat | last post by:
In Febraury - April of 2002 I put together in Java examples of all 23 of the classic "Gang Of Four" design patterns for my website. Partly I wanted to get a better understanding of those patterns....
1
by: Nick | last post by:
I have read a few months ago about a policy design pattern but can find nothing on it now. Does this design pattern exist and if so could someone explain what it is or links to a description. ...
12
by: FluffyCat | last post by:
New on November 28, 2005 for www.FluffyCat.com PHP 5 Design Pattern Examples - the Visitor Pattern. In the Visitor pattern, one class calls a function in another class and passes an instance of...
4
by: FluffyCat | last post by:
New on November 29, 2005 for www.FluffyCat.com PHP 5 Design Pattern Examples - the Command Pattern. Since you all enjoyed the Visitor Pattern so much yesterday, today I have the Command Pattern...
6
by: pitachu | last post by:
Hi, I'm not an expect in .NET, so would anyone know an answer a design pattern for the following? There are many customers that require minor customizations to the program I will be...
4
by: Guch Wu | last post by:
I want to design an image processing class as follow: class Image { Image Data; General Image Operations; read(filename, File_Type); write(filename, File_Type); };
22
by: Krivenok Dmitry | last post by:
Hello All! I am trying to implement my own Design Patterns Library. I have read the following documentation about Observer Pattern: 1) Design Patterns by GoF Classic description of Observer....
6
by: perhapscwk | last post by:
.... ... ... Both of the procedures have repeating sections of code: sending an e-mail and adding a log entry. Task: *1.Select a design pattern (substantiate your choice) that would allow...
0
by: DolphinDB | last post by:
The formulas of 101 quantitative trading alphas used by WorldQuant were presented in the paper 101 Formulaic Alphas. However, some formulas are complex, leading to challenges in calculation. Take...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
0
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
0
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.