1

I have this Food class with 20 properties. I need to use this Food class and output 3 different files, using variations of these 20 fields. For example, File 1 contains output only 8 fields. File 2 contains 15 fields. File 3 contains 18 fields.

So right now, I have these 3 separate methods.

    FoodService()
    {
        void WriteRecommendedFood(IList<Food> foodList);
        void WriteRecommendedFoodCalculation(IList<Food> foodList);
        void WriteRecommendedFoodAllEligibleFoods(IList<Food> foodList);
    }

So I'd write:

    public void WriteRecommendedFood(IList<Food> foodList)
    {
            using (StreamWriter sw = new StreamWriter("file.csv", false)
            {
                    StringBuilder sb = new StringBuilder();
                    foreach (Food f in foodList)
                    {
                            sb.Append(f.Field1); 
                            //Repeat for the # of fields I want to spit out
                            sb.Clear();
                            sw.WriteLIne(sb.ToString());
                    }
                    sw.Close();
            }
    }

I feel like I'm writing the same code three times (with slight variations). I started to read up on different design patterns like Visitor and Strategy pattern, but I'm not sure which design pattern to improve my code. (Note: I only need to output it to a comma delimited file at this time. Also, from the UI side, the user gets to select which one they want to output (either one to all 3 files.) Any suggestions?

2 Answers 2

2

It seems that the only thing that changes between these three functions is a list of fields that get written. There are several ways you can represent "a field" (and thus a list of fields) in a program; one of the most convenient is doing so as a function that extracts this field's value from a Food instance.

The type of this representation would be Func<Food, object>, so with a List<Func<Food, object>> you are good to go.

public void WriteFoodData(IEnumerable<Food> foodList, IEnumerable<Func<Food, object>> valueProviders)
{
    using (StreamWriter sw = new StreamWriter("file.csv", false))
    {
        StringBuilder sb = new StringBuilder();
        foreach (Food f in foodList)
        {
            foreach (var provider in valueProviders)
            {
                sb.Append(provider(f).ToString());
            }
            sw.WriteLIne(sb.ToString());
            sb.Clear();
        }
    }
}

Now you can create a "list of fields" and use it to call this method:

var valueProviders = new List<Func<Food, object>>
{
    f => f.Field1,
    f => f.Field4,
    // etc
};

var foods = /* whatever */
WriteFoodData(foods, valueProviders);
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you Jon! I appreciate your feedback and example!
1

I would remove the responsibility for formatting from your FoodService and inject it instead.

    public class FoodService()
    {
        public void WriteRecommendedFood(IList<Food> foodList, IFoodFormatter formatter)
        {
            using (StreamWriter sw = new StreamWriter("file.csv", false)
            {
                StringBuilder sb = new StringBuilder();
                foreach (Food f in foodList)
                {
                   sw.WriteLine(foodformatter.Format(f));
                }
                sw.Close();
            }
        }
    }

    interface IFoodFormatter
    {
        string Format(Food f);
    }

This whay you can create concrete formatters like CalculationFormatter and ElligableFoodsFormatter.

2 Comments

Thanks venerik! I like this idea too.
I ended up using this design since it was more readable to others. In addition, I also was able to modify the format of each item (especially handling some fields contains commas). Thanks again!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.