У меня есть этот класс продуктов с 20 свойствами. Мне нужно использовать этот класс Food и выпустить 3 разных файла, используя вариации этих 20 полей. Например, файл 1 содержит только 8 полей. Файл 2 содержит 15 полей. Файл 3 содержит 18 полей.
Поэтому сейчас у меня есть три разных метода.
FoodService()
{
void WriteRecommendedFood(IList<Food> foodList);
void WriteRecommendedFoodCalculation(IList<Food> foodList);
void WriteRecommendedFoodAllEligibleFoods(IList<Food> foodList);
}
Поэтому я бы написал:
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();
}
}
Мне кажется, что я пишу один и тот же код три раза (с небольшими вариациями). Я начал читать различные шаблоны дизайна, такие как шаблон Visitor и Strategy, но я не уверен, какой шаблон дизайна улучшил мой код. (Примечание: мне нужно только вывести его в файл с разделителями-запятыми в это время. Кроме того, с пользовательского интерфейса пользователь выбирает, какой из них хочет вывести (один из всех трех файлов). Любые предложения?
Я бы убрал ответственность за форматирование из вашего FoodService
и вместо этого FoodService
его.
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);
}
Это позволяет создавать конкретные форматирующие ElligableFoodsFormatter
такие как CalculationFormatter
и ElligableFoodsFormatter
.
Кажется, что единственное, что меняется между этими тремя функциями, - это список полей, которые записываются. Существует несколько способов представления "поля" (и, следовательно, списка полей) в программе; одна из наиболее удобных - это функция, которая извлекает это значение поля из экземпляра Food
.
Тип этого представления будет Func<Food, object>
, поэтому со List<Func<Food, object>>
вы хорошо пойдете.
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();
}
}
}
Теперь вы можете создать "список полей" и использовать его для вызова этого метода:
var valueProviders = new List<Func<Food, object>>
{
f => f.Field1,
f => f.Field4,
// etc
};
var foods = /* whatever */
WriteFoodData(foods, valueProviders);