什么是面向对象的编程?其实这是一个很抽象的概念。如果讲术语的话,需要解释很多,也很难去理解。那就通过一个简单的例子,去演示一下什么是面向对象编程。
1、简单的功能
假设,PM要你做一个导出到Excel的功能。
很自然,我们会定义一个 ExportService 这样的类:
public class ExportService { public void Export() { //导出Excel的逻辑 Console.WriteLine("export to excel"); } }
紧接着,我们在Main函数中,实例化这个对象,然后调用Export(),运行一下就完成了导出excel的功能。
partial class Program { private static readonly ExportService exportService = new ExportService(); static void Main(string[] args) { exportService.Export(); //======================================== Console.WriteLine("ok"); Console.ReadLine(); } }
但是,PM某一天,又说,这个地方要改一下。我们需要一个导出到PDF的功能。
那也很简单,我们只需要修改一下ExportService这个类中的Export方法:
public class ExportService { public void Export() { //导出pdf的逻辑 Console.WriteLine("export to pdf"); } }
但是这样不好,违背了 对修改封闭,对扩展开放的一个原则。
2、扩展
为了能实现面向对象的扩展能力,那么我们需要对导出进行抽象。
自然,我们会来定一个接口:
public interface IExport { void Export(); }
接着,我们来实现我们的接口:
public class ExportToExcel : IExport { public void Export() { Console.WriteLine("export to excel"); } } public class ExportToPdf : IExport { public void Export() { Console.WriteLine("export to pdf"); } }
所以,在ExportService这个类中,我们需要接收一个 IExport类型的对象。我们需要修改一下ExportService这个类
public class ExportService { private IExport _export; public ExportService(IExport export) { _export = export; } public void Export() { //导出 _export.Export(); } }
然后,在实例化对象的时候,传入一个IExport类型的参数,比如要导出Excel,我们只需要传入一个ExportToExcel的实例即可:
private static readonly ExportService exportService = new ExportService(new ExportToExcel());
同样,如果想导出到Pdf,这个地方传入一个ExportToPdf的实例即可。
这样,我们就可以不断去扩展其他导出,比如word,txt等。然后值需要在实例化的时候,传入对应的实例即可。
但是这个也不是终点。
3、IOC
我们还可以借助IOC来实现。
首先,我们可以将我们所有的服务注册到我们IOC容器中去,这里就使用dot net core下自带的容器去进行实现:
partial class Program { //private static readonly ExportService exportService = new ExportService(new ExportToPdf()); private static IServiceProvider serviceProvider; static void Main(string[] args) { RegisterService(); var exportService = serviceProvider.GetRequiredService<ExportService>();//Get service of type T from the System.IServiceProvider. exportService.Export(); //======================================== Console.WriteLine("ok"); Console.ReadLine(); } /// <summary> /// 注册服务 /// </summary> static void RegisterService() { var service = new ServiceCollection();//Microsoft.Extensions.DependencyInjection service.AddTransient<ExportService>(); service.AddTransient<IExport, ExportToExcel>(); serviceProvider = service.BuildServiceProvider(); } }
我们来运行一下程序:
可以看到,我们成功的导出到了excel。
比如我们需要一个导出到txt的功能:
public class ExportToTxt : IExport { public void Export() { Console.WriteLine("export to txt"); } }
我们只需要将这个导出到txt的服务注册到容器中即可:
/// <summary> /// 注册服务 /// </summary> static void RegisterService() { var service = new ServiceCollection();//需要引入Microsoft.Extensions.DependencyInjection service.AddTransient<ExportService>(); service.AddTransient<IExport, ExportToExcel>(); service.AddTransient<IExport, ExportToTxt>(); serviceProvider = service.BuildServiceProvider(); }
我们再来运行下程序看看:
这样我们就可以进行对导出到不同文档的功能进行扩展了。
这个地方,我们注册了txt和excel的服务,为什么只导出了txt?是因为,在同类型中,后面的会替换前面的,所以拿到的实例是最后加入的ExportToTxt。
还有一个IServiceProvider.GetServices。注释为:Get an enumeration of services of type T from the System.IServiceProvider。
这个扩展是可以每局出类型注册的所有服务。如果获取的是单一服务,优先级最高的是最后加入的。