Search code examples
c#design-patternsstrategy-pattern

What pattern can be used to create an object of one class, but fill its properties in different ways?


I have a class like this

public class OwnerWithholding
    {

        private decimal ManagementFeePct;

        private decimal TotalManagementFee;

        private decimal OperationalFeesPct;

        private decimal TotalOperationalFees;

}

And I have calculation method that create object of this class, fill it with some arithmetic operations, and return this object.

public OwnerWithholding CalculationMethod1(Reservation r, SqlConnection conn)
        {
            OwnerWithholding result = new OwnerWithholding();

           // result.ManagementFeePct  = some data from Fees table in DB + value 
           //from another db - constant value..

           // Also with other properties - with some operations on data

           //result.TotalManagementFee = ..
           // result.OperationalFeesPct = ..
           // result. TotalOperationalFees = ..

            return result;
        }

And now it works fine. But this calculation method is just one option for populating data.

There is another calculation method, implemented in a completely different way, but filling exactly the same properties of the object. And I may have more of them.

I need a pattern that would allow me to create objects of the same class, just indicating the calculation method that is needed. I like the strategy pattern , where the algorithms will be the methods that fill the objects that called them. But it doesn’t look very good. Maybe a factory method would be more appropriate here, but I don’t know how to implement it.


Solution

  • Edit: Going by the OPs comments now, it looks like just ONE method in the class needs to be set in multiple ways. The Template pattern (or the Builder) are better fits in this case, not the Factory.

    Template Pattern.

    a. Abstract Base class that set default properties, but leaves out one property (Get Ingredients) to be populated by the concrete classes.

        public abstract class PizzaCreator
        {
            public abstract string GetIngredients { get; }
            public string Bake { get; set; } = "Bake at 400F for 30 minutes";
            public string Deliver { get; set; } = "Deliver in custom box";
        }
    

    b. Two Pizza classes, for now just overriding the abstract property

        public class CheesePizza : PizzaCreator
        {
            public override string GetIngredients
            {
                get { return GetMyIngredients(); }
            }
    
            string GetMyIngredients()
            {
                return "Lots of Cheese!";
            }
        }
    
        public class PepperoniPizza : PizzaCreator
        {
            public override string GetIngredients
            {
                get { return GetMyIngredients(); }
            }
    
            string GetMyIngredients()
            {
                return "Lots of Meats!";
            }
        }
    

    Here I'm creating instances of the Pizza

                var pepPizza = new PepperoniPizza();
                var chessePizza = new CheesePizza();
    

    You could even have these creations routed through a Factory class/method.

    Original answer: Here is the Abstract Factory Pattern.

    This code goes into the Factory class library.

    a.ICar interface

        public interface ICar
        {
            string Name { get; set; }
            int Doors { get; set; }
            string EngineCapacity { get; set; }
        }
    

    b.Abstract Car Factory

        public abstract class AbstractCarFactory
        {
            public abstract ICar CreateCar(CarType type);
        }
    

    c.Two Concrete Cars -

        internal class NissanPickUpTruck : ICar
        {
            public string Name { get; set; } 
            public int Doors { get; set ; }
            public string EngineCapacity { get ; set ; }
        }
    
        internal class NissanSportsCar: ICar
        {
            public string Name { get; set; } 
            public int Doors { get; set; }
            public string EngineCapacity { get; set; }
        }
    

    d.Concrete Factory

        public class NissanFactory : AbstractCarFactory
        {
            public override ICar CreateCar(CarType type)
            {
                switch (type)
                {
                    case CarType.PickupTruck:
                        return new NissanPickUpTruck{Name = "Titan", Doors = 6, EngineCapacity = "V12"};
                    case CarType.SportsCar:
                        return new NissanSportsCar{Name = "350Z", Doors = 2, EngineCapacity = "V6"};
                    default:
                        throw new Exception();
                }
            }
        }
    

    Finally the calls from an external project

                var nissanFactory = new NissanFactory();
                var sportsCar = nissanFactory.CreateCar(CarType.SportsCar);
                var pickUpTruck = nissanFactory.CreateCar(CarType.PickupTruck);
    

    But like the other comment, the Builder is something worth checking out as well.