so I am trying to write a discounts method that will apply discount(s) on a product.
The current vanilla code goes like so:
void ApplyDiscount(List<DiscountRule> discountRules, Product objProduct)
{
foreach (var discountRule in discountRules)
{
// this is a very simple way of deciding on the available discounts
if (discountRule.Type==DiscountType.Percent)
{
// Process for percentage discount
}
if (discountRule.Type==DiscountType.Free)
{
// Process for flat discount
}
// and so on , there are like 5 more types,
// not mentioned here for the case of brevity.
}
}
What this method does is take a list of discount rules and apply on the product.
The discount rules are fetched by executing a SP # the server and that returns the
available discounts for that product.
The code review for this resulted in the following comment:
Please use an interface based approach and try to get rid of the IFs!
I can get rid of the IFs but they will be replaced by SWITCH.
How do I go about using an interface?
May be this question is not constructive, but I would want to know if some OOPs gurus here can guide me in writing this better.
Regards.
An interface / virtual-dispatch approach might look something like this:
// First we loosely define how a "discount" can be used.
// This could also be an abstract class, if common base-class
// functionality is desired.
public interface IDiscount
{
// This is called to apply this discount to a particular product
void ApplyDiscount(Product product);
}
// Here's one implementation that applies a percentage discount
public class PercentDiscount : IDiscount
{
private decimal m_percent;
public PercentDiscount(decimal percent) {
m_percent = percent;
}
#region IDiscount implementation
public void ApplyDiscount(Product product) {
product.Price -= product.Price * m_discount;
}
#endregion
}
// Here's another implementation that makes a product free
public class FreeDiscount : IDiscount
{
public FreeDiscount() {
}
#region IDiscount implementation
public void ApplyDiscount(Product product) {
product.Price = 0;
}
#endregion
}
public class SomeClass {
// Now applying the discounts becomes much simpler! Note that this function
// takes a collection of IDiscounts, and applies them in a consistent way,
// by just calling IDiscount.ApplyDiscount()
void ApplyDiscounts(IEnumerable<IDiscount> discounts, Product product) {
foreach (var discount in discounts) {
discount.ApplyDiscount(product);
}
}
}
Note that I also changed ApplyDiscounts to take an IEnumerable<T> instead of List<T>. This allows any arbitrary collection type to be passed, and also doesn't allow the function to inadvertently modify the collection.
Related
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 months ago.
Improve this question
There is an interface called Processor, which has two implementations SimpleProcessor and ComplexProcessor.
Now I have a process, which consumes an input, and then using that input decides whether it should use SimpleProcessor or ComplexProcessor.
Current solution : I was thinking to use Abstract Factory, which will generate the instance on the basis of the input.
But the issue is that I don't want new instances. I want to use already instantiated objects. That is, I want to re-use the instances.
That means, Abstract factory is absolutely the wrong pattern to use here, as it is for generating objects on the basis of type.
Another thing, that our team normally does is to create a map from input to the corresponding processor instance. And at runtime, we can use that map to get the correct instance on the basis of input.
This feels like a adhoc solution.
I want this to be extendable : new input types can be mapped to new processor types.
Is there some standard way to solve this?
You can use a variation of the Chain of Responsibility pattern.
It will scale far better than using a Map (or hash table in general).
This variation will support dependency injection and is very easy to extend (without breaking any code or violating the Open-Closed principle).
Opposed to the classic version, handlers do not need to be explicitly chained. The classic version scales very bad.
The pattern uses polymorphism to enable extensibility and is therefore targeting an object oriented language.
The pattern is as follows:
The client API is a container class, that manages a collection of input handlers (for example SimnpleProcessor and ComplexProcessor).
Each handler is only known to the container by a common interface and unknown to the client.
The collection of handlers is passed to the container via the constructor (to enable optional dependency injection).
The container accepts the predicate (input) and passes it on to the anonymous handlers by iterating over the handler collection.
Each handler now decides based on the input if it can handle it (return true) or not (return false).
If a handler returns true (to signal that the input was successfully handled), the container will break further input processing by other handlers (alternatively, use a different criteria e.g., to allow multiple handlers to handle the input).
In the following very basic example implementation, the order of handler execution is simply defined by their position in their container (collection).
If this isn't sufficient, you can simply implement a priority algorithm.
Implementation (C#)
Below is the container. It manages the individual handler implementation using polymorphism. Since handler implementation are only known by their common interface, the container scales extremely well: simply add/inject an additional handler implementation.
The container is actually used directly by the client (whereas the handlers are hidden from the client, while anonymous to the container).
interface IInputProcessor
{
void Process(object input);
}
class InputProcessor : IInputProcessor
{
private IEnumerable<IInputHandler> InputHandlers { get; }
// Constructor.
// Optionally use an IoC container to inject the dependency (a collection of input handlers).
public InputProcessor(IEnumerable<IInputHandler> inputHandlers)
{
this.InputHandlers = inputHandlers;
}
// Method to handle the input.
// The input is then delegated to the input handlers.
public void Process(object input)
{
foreach (IInputHandler inputHandler in this.InputHandlers)
{
if (inputHandler.TryHandle(input))
{
return;
}
}
}
}
Below are the input handlers.
To add new handlers i.e. to extend input handling, simply implement the IInputHandler interface and add it to a collection which is passed/injected to the container (IInputProcessor):
interface IInputHandler
{
bool TryHandle(object input);
}
class SimpleProcessor : IInputHandler
{
public bool TryHandle(object input)
{
if (input == 1)
{
//TODO::Handle input
return true;
}
return false;
}
}
class ComplexProcessor : IInputHandler
{
public bool TryHandle(object input)
{
if (input == 3)
{
//TODO::Handle input
return true;
}
return false;
}
}
Usage Example
public class Program
{
public static void Main()
{
/* Setup Chain of Responsibility.
/* Preferably configure an IoC container. */
var inputHandlers = new List<IInputHandlers>
{
new SimpleProcessor(),
new ComplexProcessor()
};
IInputProcessor inputProcessor = new InputProcessor(inputHandlers);
/* Use the handler chain */
int input = 3;
inputProcessor.Pocess(input); // Will execute the ComplexProcessor
input = 1;
inputProcessor.Pocess(input); // Will execute the SimpleProcessor
}
}
It is possible to use Strategy pattern with combination of Factory pattern. Factory objects can be cached to have reusable objects without recreating them when objects are necessary.
As an alternative to caching, it is possible to use singleton pattern. In ASP.NET Core it is pretty simple. And if you have DI container, just make sure that you've set settings of creation instance to singleton
Let's start with the first example. We need some enum of ProcessorType:
public enum ProcessorType
{
Simple, Complex
}
Then this is our abstraction of processors:
public interface IProcessor
{
DateTime DateCreated { get; }
}
And its concrete implemetations:
public class SimpleProcessor : IProcessor
{
public DateTime DateCreated { get; } = DateTime.Now;
}
public class ComplexProcessor : IProcessor
{
public DateTime DateCreated { get; } = DateTime.Now;
}
Then we need a factory with cached values:
public class ProcessorFactory
{
private static readonly IDictionary<ProcessorType, IProcessor> _cache
= new Dictionary<ProcessorType, IProcessor>()
{
{ ProcessorType.Simple, new SimpleProcessor() },
{ ProcessorType.Complex, new ComplexProcessor() }
};
public IProcessor GetInstance(ProcessorType processorType)
{
return _cache[processorType];
}
}
And code can be run like this:
ProcessorFactory processorFactory = new ProcessorFactory();
Thread.Sleep(3000);
var simpleProcessor = processorFactory.GetInstance(ProcessorType.Simple);
Console.WriteLine(simpleProcessor.DateCreated); // OUTPUT: 2022-07-07 8:00:01
ProcessorFactory processorFactory_1 = new ProcessorFactory();
Thread.Sleep(3000);
var complexProcessor = processorFactory_1.GetInstance(ProcessorType.Complex);
Console.WriteLine(complexProcessor.DateCreated); // OUTPUT: 2022-07-07 8:00:01
The second way
The second way is to use DI container. So we need to modify our factory to get instances from dependency injection container:
public class ProcessorFactoryByDI
{
private readonly IDictionary<ProcessorType, IProcessor> _cache;
public ProcessorFactoryByDI(
SimpleProcessor simpleProcessor,
ComplexProcessor complexProcessor)
{
_cache = new Dictionary<ProcessorType, IProcessor>()
{
{ ProcessorType.Simple, simpleProcessor },
{ ProcessorType.Complex, complexProcessor }
};
}
public IProcessor GetInstance(ProcessorType processorType)
{
return _cache[processorType];
}
}
And if you use ASP.NET Core, then you can declare your objects as singleton like this:
services.AddSingleton<SimpleProcessor>();
services.AddSingleton<ComplexProcessor>();
Read more about lifetime of an object
I am trying to clean and refactor my service code which currently looks like this-
public void generateBalance(Receipt receipt) {
if (receipt.getType().equals(X) && receipt.getRegion.equals(EMEA)) {
// do something to the receipt that's passed
} else if (receiptType.equals(Y)) {
// do something to the receipt
} else if (receipt.getRegion.equals(APAC) {
// call an external API and update the receipt
}....
...
// finally
dataStore.save(receipt);
Basically there's a bunch of conditionals that are in this main service which look for certain fields in the object that is being passed. Either it's the type or the region.
I was looking to use this design pattern- https://www.refactoring.com/catalog/replaceConditionalWithPolymorphism.html
However, I am not sure how this would work for a service class. Currently my REST handler calls this particular service. Also how can I do polymorphism for both the "receiptType" and "region"?
Is there a way I can just do all the updates to the receipt once in different services, then finally save the receipt at one location? (maybe a base class?) I am really confused on how to start. TIA!
If your classes should have the same behaviour, then it becomes pretty simple to use polymorpism. The pattern is called as Strategy. Let me show an example.
At first we need to use enum. If you do not have enum, then you can create a method which will return enum value based on your conditions:
if (receipt.getType().equals(X) && receipt.getRegion.equals(EMEA)) // other
// code is omitted for the brevity
So enum will look like this:
public enum ReceiptType
{
Emea, Y, Apac
}
Then we need an abstract class which will describe behaviour for derived classes:
public abstract class ActionReceipt
{
public abstract string Do();
}
And our derived classes will look this:
public class ActionReceiptEmea : ActionReceipt
{
public override string Do()
{
return "I am Emea";
}
}
public class ActionReceiptY : ActionReceipt
{
public override string Do()
{
return "I am Y";
}
}
public class ActionReceiptApac : ActionReceipt
{
public override string Do()
{
return "I am Apac";
}
}
Moreover, we need a factory which will create derived classes based on enum. So we can use Factory pattern with a slight modification:
public class ActionReceiptFactory
{
private Dictionary<ReceiptType, ActionReceipt> _actionReceiptByType =
new Dictionary<ReceiptType, ActionReceipt>
{
{
ReceiptType.Apac, new ActionReceiptApac()
},
{
ReceiptType.Emea, new ActionReceiptEmea()
},
{
ReceiptType.Y, new ActionReceiptY()
}
};
public ActionReceipt GetInstanceByReceiptType(ReceiptType receiptType) =>
_actionReceiptByType[receiptType];
}
And then polymorpism in action will look like this:
void DoSomething(ReceiptType receiptType)
{
ActionReceiptFactory actionReceiptFactory = new ActionReceiptFactory();
ActionReceipt receipt =
actionReceiptFactory.GetInstanceByReceiptType(receiptType);
string someDoing = receipt.Do(); // Output: "I am Emea"
}
UPDATE:
You can create some helper method which will return enum value based on
your logic of region and receiptType:
public class ReceiptTypeHelper
{
public ReceiptType Get(ActionReceipt actionReceipt)
{
if (actionReceipt.GetType().Equals("Emea"))
return ReceiptType.Emea;
else if (actionReceipt.GetType().Equals("Y"))
return ReceiptType.Y;
return ReceiptType.Apac;
}
}
and you can call it like this:
void DoSomething()
{
ReceiptTypeHelper receiptTypeHelper = new ReceiptTypeHelper();
ReceiptType receiptType = receiptTypeHelper
.Get(new ActionReceiptEmea());
ActionReceiptFactory actionReceiptFactory = new
ActionReceiptFactory();
ActionReceipt receipt =
actionReceiptFactory.GetInstanceByReceiptType(receiptType);
string someDoing = receipt.Do(); // Output: "I am Emea"
}
i need Prestashop to:
Check stock of combinations from a product.
If a combination is out of stock, set default combination to a different combination which is in stock.
This way the shop will not show : 'out of stock' at the product, on the category listing. Makes no sense, because the product is not out of stock, only 1 combination is out of stock.
Another solution will be: the out of stock sticker to check if there are any combinations in stock.
Please do not advise any modules.
Example given:
T-shirt in size Small, Medium and Large.
Small: 0 stock, Medium: 10 stock, Large: 10 stock.
Prestashop will now show T-shirt as out of stock on the frontend, unless i manually check Medium or Large as a default size.
You will need to modify ProductController(/controllers/front/ProductController.php)
Basically what i did was to check if the default attribute has stock. If there is stock,then there is no need to look else where.
If there is no stock, then get all the product attributes and loop through the attributes. Once attribute with stock is found, delete the old default attribute and set the found as default attribute.
Add this methods inside the productController Class
/*
start set default based on stock
*/
public function getProductAttributes()
{
$sql = "SELECT * FROM " . _DB_PREFIX_ . "product_attribute WHERE
id_product = ". (int) $this->product->id;
return Db::getInstance()->executeS($sql);
}
public function getProductStock($id_product_attribute)
{
return StockAvailable::getQuantityAvailableByProduct($this->product->id,
$id_product_attribute, $this->context->shop->id);
}
public function checkDefault()
{
$id_product_attribute = Product::getDefaultAttribute($this->product->id);
$stk = (int) $this->getProductStock($id_product_attribute);
if( $stk > 0){
return true;
}
return false;
}
public function checkAttributeStock( $id_product_attribute)
{
$stk = $this->getProductStock($id_product_attribute);
if( $stk > 0){
return true;
}
return false;
}
public function setDefaultBasedOnStock()
{
if($this->checkDefault()){
return true;
}
$product_atts = $this->getProductAttributes();
if(count($product_atts) >0){
foreach($product_atts as $attri){
if($this->checkAttributeStock($attri['id_product_attribute'])){
$this->product->deleteDefaultAttributes();
$this->product->setDefaultAttribute($attri['id_product_attribute']);
break;
}
}
}
}
/*
end set default based on stock
*/
Then you need to call setDefaultBasedOnStock method inside init Method just after parent::init();
public function init()
{
parent::init();
$this->setDefaultBasedOnStock();
// here is the rest of init code ......
//Do not delete the rest of init code
}
Please do not delete the rest of init() method code, just add this line $this->setDefaultBasedOnStock(); below parent::init();
The best way will be to override productController
Instead of modifying productController class, you can create productController override class inside /override/controllers/front/ folder
class ProductController extends ProductControllerCore {
// add all the methods above
// including the init method
}
// After add override you may need to clear cache if prestashop cache is enabled.
I have a GiftCouponPayment class. It has a business strategy logic which can change frequently - GetCouponValue(). At present the logic is “The coupon value should be considered as zero when the Coupon Number is less than 2000”. In a future business strategy it may change as “The coupon value should be considered as zero when the Coupon Issued Date is less than 1/1/2000”. It can change to any such strategies based on the managing department of the company.
How can we refactor the GiftCouponPayment class using Strategy pattern so that the class need not be changed when the strategy for GetCouponValue method?
UPDATE: After analyzing the responsibilities, I feel, "GiftCoupon" will be a better name for "GiftCouponPayment" class.
C# CODE
public int GetCouponValue()
{
int effectiveValue = -1;
if (CouponNumber < 2000)
{
effectiveValue = 0;
}
else
{
effectiveValue = CouponValue;
}
return effectiveValue;
}
READING
Strategy Pattern - multiple return types/values
GiftCouponPayment class should pass GiftCoupon to different strategy classes. So your strategy interface (CouponValueStrategy) should contain a method:
int getCouponValue(GiftCoupon giftCoupon)
Since each Concrete strategy implementing CouponValueStrategy has access to GiftCoupon, each can implement an algorithm based on Coupon number or Coupon date etc.
You can inject a "coupon value policy" into the coupon object itself and call upon it to compute the coupon value. In such cases, it is acceptable to pass this into the policy so that the policy can ask the coupon for its required attributes (such as coupon number):
public interface ICouponValuePolicy
{
int ComputeCouponValue(GiftCouponPayment couponPayment);
}
public class GiftCouponPayment
{
public ICouponValuePolicy CouponValuePolicy {
get;
set;
}
public int GetCouponValue()
{
return CouponValuePolicy.ComputeCouponValue(this);
}
}
Also, it seems like your GiftCouponPayment is really responsible for two things (the payment and the gift coupon). It might make sense to extract a GiftCoupon class that contains CouponNumber, CouponValue and GetCouponValue(), and refer to this from the GiftCouponPayment.
When your business - logic changes, it's quite natural that your code will have to change as well.
You could perhaps opt to move the expiration-detection logic into a specification class:
public class CouponIsExpiredBasedOnNumber : ICouponIsExpiredSpecification
{
public bool IsExpired( Coupon c )
{
if( c.CouponNumber < 2000 )
return true;
else
return false;
}
}
public class CouponIsExpiredBasedOnDate : ICouponIsExpiredSpecification
{
public readonly DateTime expirationDate = new DateTime (2000, 1, 1);
public bool IsExpired( Coupon c )
{
if( c.Date < expirationDate )
return true;
else
return false;
}
}
public class Coupon
{
public int GetCouponValue()
{
ICouponIsExpiredSpecification expirationRule = GetExpirationRule();
if( expirationRule.IsExpired(this) )
return 0;
else
return this.Value;
}
}
The question you should ask yourself: is it necessary to make it this complex right now ? Can't you make it as simple as possible to satisfy current needs, and refactor it later, when the expiration-rule indeed changes ?
The behavior that you wish to be dynamic is the coupon calculation - which can dependent on any number of things: coupon date, coupon number, etc. I think that a provider pattern would be more appropriate, to inject a service class which calculates the coupon value.
The essence of this is moving the business logic outside of the GiftCouponPayment class, and using a class I'll call "CouponCalculator" to encapsulate the business logic. This class uses an interface.
interface ICouponCalculator
{
int Calculate (GiftCouponPayment payment);
}
public class CouponCalculator : ICouponCalculator
{
public int Calculate (GiftCouponPayment payment)
{
if (payment.CouponNumber < 2000)
{
return 0;
}
else
{
return payment.CouponValue;
}
}
}
Now that you have this interface and class, add a property to the GiftCouponPayment class, then modify your original GetCouponValue() method:
public class GiftCouponPayment
{
public int CouponNumber;
public int CouponValue;
public ICouponCalculator Calculator { get; set; }
public int GetCouponValue()
{
return Calculator.Calculate(this);
}
}
When you construct the GiftCouponPayment class, you will assign the Calculator property:
var payment = new GiftCouponPayment() { Calculator = new CouponCalculator(); }
var val = payment.GetCouponValue(); // uses CouponCalculator class to get value
If this seems like a lot of work just to move the calculation logic outside of the GiftCouponPayment class, well, it is! But if this is your requirement, it does provide several things:
1. You won't need to change the GiftCouponPayment class to adjust the calculation logic.
2. You could create additional classes that implement ICalculator, and a factory pattern to decide which class to inject into GiftCouponPayment when it is constructed. This speaks more to your original desire for a "strategy" pattern - as this would be useful if the logic becomes very complex.
I built a custom collector for Lucene.Net, but I can't figure out how to order (or page) the results. Everytime Collect gets called, I can add the result to an internal PriorityQueue, which I understand is the correct way to do this.
I extended the PriorityQueue, but it requires a size parameter on creation. You have to call Initialize in the constructor and pass in the max size.
However, in a collector, the searcher just calls Collect when it gets a new result, so I don't know how many results I have when I create the PriorityQueue. Based on this, I can't figure out how to make the PriorityQueue work.
I realize I'm probably missing something simple here...
PriorityQueue is not SortedList or SortedDictionary.
It is a kind of sorting implementation where it returns the top M results(your PriorityQueue's size) of N elements. You can add with InsertWithOverflow as many items as you want, but it will only hold only the top M elements.
Suppose your search resulted in 1000000 hits. Would you return all of the results to user?
A better way would be to return the top 10 elements to the user(using PriorityQueue(10)) and
if the user requests for the next 10 result, you can make a new search with PriorityQueue(20) and return the next 10 elements and so on.
This is the trick most search engines like google uses.
Everytime Commit gets called, I can add the result to an internal PriorityQueue.
I can not undestand the relationship between Commit and search, Therefore I will append a sample usage of PriorityQueue:
public class CustomQueue : Lucene.Net.Util.PriorityQueue<Document>
{
public CustomQueue(int maxSize): base()
{
Initialize(maxSize);
}
public override bool LessThan(Document a, Document b)
{
//a.GetField("field1")
//b.GetField("field2");
return //compare a & b
}
}
public class MyCollector : Lucene.Net.Search.Collector
{
CustomQueue _queue = null;
IndexReader _currentReader;
public MyCollector(int maxSize)
{
_queue = new CustomQueue(maxSize);
}
public override bool AcceptsDocsOutOfOrder()
{
return true;
}
public override void Collect(int doc)
{
_queue.InsertWithOverflow(_currentReader.Document(doc));
}
public override void SetNextReader(IndexReader reader, int docBase)
{
_currentReader = reader;
}
public override void SetScorer(Scorer scorer)
{
}
}
searcher.Search(query,new MyCollector(10)) //First page.
searcher.Search(query,new MyCollector(20)) //2nd page.
searcher.Search(query,new MyCollector(30)) //3rd page.
EDIT for #nokturnal
public class MyPriorityQueue<TObj, TComp> : Lucene.Net.Util.PriorityQueue<TObj>
where TComp : IComparable<TComp>
{
Func<TObj, TComp> _KeySelector;
public MyPriorityQueue(int size, Func<TObj, TComp> keySelector) : base()
{
_KeySelector = keySelector;
Initialize(size);
}
public override bool LessThan(TObj a, TObj b)
{
return _KeySelector(a).CompareTo(_KeySelector(b)) < 0;
}
public IEnumerable<TObj> Items
{
get
{
int size = Size();
for (int i = 0; i < size; i++)
yield return Pop();
}
}
}
var pq = new MyPriorityQueue<Document, string>(3, doc => doc.GetField("SomeField").StringValue);
foreach (var item in pq.Items)
{
}
The reason Lucene's Priority Queue is size limited is because it uses a fixed size implementation that is very fast.
Think about what is the reasonable maximum number of results to get back at a time and use that number, the "waste" for when the results are few is not that bad for the benefit it gains.
On the other hand, if you have such a huge number of results that you cannot hold them, then how are you going to be serving/displaying them? Keep in mind that this is for "top" hits so as you iterate through the results you will be hitting less and less relevant ones anyway.