如何解决开闭原则c#
我有 3 个类(1 个抽象(服务)和 2 个派生类)。另外,我有 2 种不同的输出格式(UA/EN)。而且我不知道如何重新编写我的代码以使其遵循开放封闭原则。例如,如果我想添加德语输出格式。我需要编辑每个课程。
using System;
using System.Globalization;
namespace naslidov
{
public abstract class Services
{
public string title;
public decimal price;
public Services(string title,decimal price)
{
this.title = title;
this.price = price;
}
public virtual string ToEnglish()
{
return $" ";
}
public virtual string ToUkraine()
{
return $"";
}
}
public class Food : Services
{
public DateTime expirationDate;
public Food(string title,decimal price,DateTime expirationDate)
: base(title,price)
{
this.title = title;
this.price = price;
this.expirationDate = expirationDate;
}
public override string ToEnglish()
{
return $"{base.title} |{price.ToString("N",CultureInfo.InvariantCulture)} uan | {expirationDate.ToString("d",CultureInfo.CreateSpecificCulture("ja-JP"))} |------ ";
}
public override string ToUkraine()
{
return $"{base.title} |{price.ToString("F",CultureInfo.InvariantCulture).Replace(".",",")} uan | {expirationDate.ToString("dd.MM.yyyy")}| ------ ";
}
}
public class HouseholdAppliance : Services
{
public int warrantyPeriodInMonths;
public HouseholdAppliance(string title,int warrantyPeriodInMonths)
: base(title,price)
{
this.title = title;
this.price = price;
this.warrantyPeriodInMonths = warrantyPeriodInMonths;
}
public override string ToEnglish()
{
return $"{base.title} |{price.ToString("N",CultureInfo.InvariantCulture)} uan | ------ |{warrantyPeriodInMonths} ";
}
public override string ToUkraine()
{
return $"{base.title} |{price.ToString("F",")} uan | ------ |{warrantyPeriodInMonths} ";
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enter region(UA/EN):");
string user_input = Console.ReadLine();
DateTime date1 = new DateTime(2002,3,25);
DateTime date2 = new DateTime(2022,8,17);
DateTime date3 = new DateTime(2005,1,10);
Services first = new Food("apple",105324660120.58m,date1);
Services second = new Food("bananas",3045.21m,date2);
Services third = new Food("nuts",308540m,date3);
Services nofrst = new HouseholdAppliance("television",25547.54m,12);
Services noscd = new HouseholdAppliance("pilosos",2756854m,24);
Services nothir = new HouseholdAppliance("notebook",32248,36);
Services[] fullservices = new Services[] { first,second,third,nofrst,noscd,nothir };
Console.WriteLine("title | price | expirationDate | warrantyPeriodInMonths");
Console.WriteLine("-----------------------------------------------------------------------");
if (user_input == "EN")
{
foreach (Services fullservice in fullservices)
{
Console.WriteLine(fullservice.ToEnglish());
}
}
if (user_input == "UA")
{
foreach (Services fullservice in fullservices)
{
Console.WriteLine(fullservice.ToUkraine());
}
}
else if (user_input != "UA" && user_input != "EN")
{
Console.WriteLine(" Sorry,wrong input!");
}
}
}
}
解决方法
首先,我想鼓励永远不要在没有要求或目标的情况下进行重构。如果您正在尝试重构此代码以使其可扩展以用于您不知道需要扩展的内容,那么您不仅可能会浪费精力(YAGNI),而且您还可能最终得到更难更改的代码您以后可能需要的其他方式。
因此,就本回答而言,我将假设要求是使此代码可扩展(对扩展开放)。而您需要扩展的是支持的格式。
我们将首先定义一个新的 抽象类 接口 Formatter
IFormat
,它将作为添加新格式的扩展点。理想情况下,这个 IFormat
不应该依赖于任何特定的(具体的,而不是抽象的)Services
,也不应该 Services
知道任何特定的 IFormat
。也就是说,我们希望将这些扩展为尽可能独立。
现在,特定的 Services
需要格式化什么?我可以在代码中看到您需要知道日期和价格的格式。因此,让我们提供将这些格式设置为 IFormat
的方法:
public interface IFormat
{
string FormatDate(DateTime date);
string FormatPrice(decimal price);
}
添加任何其他有意义的方法。我为这种情况添加了最小值。
我们可以继续为英语和乌克兰语实现一个格式化程序。 请原谅我的命名习惯。
public class FormatterEnglish : IFormat
{
public string FormatDate(DateTime date)
{
return date.ToString("d",CultureInfo.CreateSpecificCulture("ja-JP"));
}
public string FormatPrice(decimal price)
{
return price.ToString("N",CultureInfo.InvariantCulture);
}
}
public class FormatterUkrane : IFormat
{
public string FormatDate(DateTime date)
{
return date.ToString("dd.MM.yyyy");
}
public string FormatPrice(decimal price)
{
return price.ToString("F",CultureInfo.InvariantCulture).Replace(".",",");
}
}
现在,让我们重新编写 Services
以使用它。每种格式不再有一种方法,而是一种采用 IFormat
参数的方法:
public abstract class Services
{
public decimal price;
public string title;
public Services(string title,decimal price)
{
this.title = title;
this.price = price;
}
public abstract string ToString(IFormat formatter);
}
当然,我们需要在 HouseholdAppliance
中实现它:
public override string ToString(IFormat formatter)
{
return $"{base.title} |{formatter.FormatPrice(price)} uan | ------ |{warrantyPeriodInMonths} ";
}
还有Food
:
public override string ToString(IFormat formatter)
{
return $"{base.title} |{formatter.FormatPrice(price)} uan | {formatter.FormatDate(expirationDate)} |------ ";
}
要选择我们的 IFormat
,我建议使用工厂方法。例如:
private static IFormat? CreateFormatter(string formatName)
{
if (formatName == "EN")
{
return new FormatterEnglish();
}
if (formatName == "UA")
{
return new FormatterUkrane();
}
return null;
}
您可能还对使用类型发现和在自定义属性中指定格式名称感兴趣。这超出了本答案的范围。
最后,你可以这样使用它:
var formatter = CreateFormatter(user_input);
if (formatter == null)
{
Console.WriteLine(" Sorry,wrong input!");
return;
}
foreach (Services fullservice in fullservices)
{
Console.WriteLine(fullservice.ToString(formatter));
}
再次查看代码,可以从 Services
中提取 format 模板。 IFormat
需要模板和数据。像 FormatWith
这样的解决方案会使这更容易。无论如何,我相信这个答案可以解决问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。