1
\$\begingroup\$

A purchase order has an order ID, a customer ID, total price and line items. Line items can be a product or membership type.

Business rules:

  1. If the purchase order contains a membership, it has to be activated in the customer account immediately.
  2. If the purchase order contains "Comprehensive First Aid training video," then "Basic First Aid training video" is added to the purchase order
public abstract class Product
{
}
 public class BookClubMembership : Product
{
    public bool IsActivated { get; set; }        
}

Using the strategy design pattern, I define the first rule as follows:

public class BookClubMembershipRule : IPurchaseOrderRule
{
    readonly ICustomerActivationService _customerActivationService;

    public BookClubMembershipRule(ICustomerActivationService customerActivationService)
    {
        _customerActivationService = customerActivationService;
    }
    public void Execute(PurchaseOrder order)
    {
        foreach(var product in order.Items) {
            if (product is BookClubMembership)
            {
                _customerActivationService.ActivateBookMembership(order.CustomerId, product as BookClubMembership);
                break;
            }
        }

    }
}

public class PurchaseOrderProcessor
{
    public IList<IPurchaseOrderRule> Rules { get; }

    public PurchaseOrderProcessor(List<IPurchaseOrderRule> rules)
    {
        Rules = rules;
    }

    public void ProcessOrder(PurchaseOrder order)
    {
        foreach (var rule in Rules)
            rule.Execute(order);
    }

I don't feel comfortable using C# is and as above. Is there a better way for me to program these rules. I was thinking of Visitor pattern but given the 2nd rule. I will need to operate on the order and the item as well.

A line item is what I call an item in an order. For example, an order can look like this:

Order No: 3344656
 Total: 48.50 
 Customer Id: 4567890 
 Line items:
  • Video "Comprehensive First Aid Training" 
  • Book "The Girl on the train" 
  • Book Club Membership 

Added code for the second rule:

public class ComprehensiveVideoRule : IPurchaseOrderRule
{
    public void Execute(PurchaseOrder order)
    {
        bool addNewVideo = false;
        foreach(var item in order.Items)
        {
            if(item is Video)
            {
                var checkItem = item as Video;
                if(checkItem.Title.Contains("Comprehensive"))
                    addNewVideo = true;
            }
        }
        if(addNewVideo) order.Items.Add(new Video("Basic First Aid"));
    }
}
\$\endgroup\$
  • \$\begingroup\$ I'm quite tired now for full fledge review, so just will leave small comment here: It's seems that ActivateBookMembership method is saving something in Database, consider using async code for such operations, this would allow your application scale. \$\endgroup\$ – Bogdan Mart Jul 13 '17 at 21:37
1
\$\begingroup\$

There's no point in an abstract class with nothing in it. I assume a product has name, description, price, a SKU (product id) at least.


Line items can be a product or membership type.

implies that a membership is not a product yet I see

public class BookClubMembership : Product

Looks like you'll have every rule iterate the order items. I'd rather see the PurchaseOrderProcessor iterate the items; and only once.


public class PurchaseOrderProcessor {
   public IList<IPurchaseOrderRule> Rules { get; }

The Rules list should not be public. Hide state and expose functionality. If you need to add rules for example, make an Add method.


Is there a better way for me to program these rules

The Product can have a "type" - with all the types defined in an enum. This eliminates the need for subclasses.


What is a 'line item'? If it means something I'd expect a class. Use your domain language accurately.


... I will need to operate on the order and the item as well.

Show me the codez!


_customerActivationService.ActivateBookMembership(order.CustomerId,

Is it a valid assumption that the membership is for the person making the purchase?

\$\endgroup\$
  • \$\begingroup\$ You have my vote but this advice The Product can have a "type" is questionable. I think having subclasses for each product is much better since they can have different properties and you won't be able to cover them with a common class and if you do you'd have a bunch of unnecessasry ones for products that don't support them. \$\endgroup\$ – t3chb0t Jul 15 '17 at 7:11
  • \$\begingroup\$ you made a lot of good points here. The membership being a product is because it is something that can be bought. it has common attributes of a product. price, name, description. \$\endgroup\$ – Francis Ade Jul 15 '17 at 12:36
  • \$\begingroup\$ @FrancisAde it has common attributes of a product. price, name, description oh, really? there is nothing about it in the question. \$\endgroup\$ – t3chb0t Jul 15 '17 at 12:42
  • \$\begingroup\$ Sorry about that. my focus and confusion was on how to code the rules.. I assume intricate details about a line item is not necessary. i was clearly wrong \$\endgroup\$ – Francis Ade Jul 15 '17 at 13:00
  • \$\begingroup\$ re: a 'type' property vis-a-vis subclass. Granting @tcchb0t comment, I am and was ambivalent about the suggestion - I really don't know what's up with a Product at this point. But it is a way to curtail subclass explosion when there is much commonality. \$\endgroup\$ – radarbob Jul 15 '17 at 23:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.