C# - 面向对象 - 接口


C# - 面向对象 - 接口

面向接口编程 —— 什么是接口

  • 接口与抽象类类似,仅作功能声明不做代码实现的语法结构。
  • interface关键词,同时接口前固定格式“I”+“XXX”

为什么需要接口

  • 根源上解耦,使系统之间的耦合减到最小,甚至耦合为零。

代码例子

接口代码实现

新建接口

namespace HelloWord
{
    public interface IShippingCalculator
    {
        float CalculateShipping(Order order);
    }
}

其他类要使用接口,则需要使用 “:”继承接口

namespace HelloWord
{
    internal class DoubleElevenShippingCalculator : IShippingCalculator
    {
        public float CalculateShipping(Order order)
        {
            return 0;
        }
    }
}

实例 (有用到IShippingCalculator的部分)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace HelloWord
{
    class OrderProcessor
    {
        private readonly IShippingCalculator _shippingCalculator;

        public OrderProcessor(IShippingCalculator shippingCalculator)
        {
            _shippingCalculator = shippingCalculator;
        }

        
        public void Process(Order order)
        {
            if (order.IsShipped)
                throw new InvalidOperationException("订单已发货");

            order.Shipment = new Shipment
            {
                Cost = _shippingCalculator.CalculateShipping(order),
                ShippingDate = DateTime.Today.AddDays(1)
            };
            Console.WriteLine($"订单#{order.Id}完成,已发货");
        }
    }
}
using System;

namespace HelloWord
{

    class Program
    {
        static void Main(string[] args)
        {
            var order = new Order
            {
                Id = 123,
                DatePlaced = DateTime.Now,
                TotalPrice = 100f
            };

            IShippingCalculator doubleElven = new DoubleElevenShippingCalculator();

            IShippingCalculator normal = new ShippingCalculator();
            var orderProcessor = new OrderProcessor(doubleElven);

            if (DateTime.Now != new DateTime(2050, 11, 11)) {
                orderProcessor = new OrderProcessor(normal);
            }

            orderProcessor.Process(order);

            Console.Read();
        }
    }
}

接口与单元测试

新建“MSTest”测试项目

  • 注意:“新建项目”该菜单在“解决方案”中,而非项目中。
  • 注意:测试项目名称最好遵循的格式:”项目名称“+”UnitTests“

默认“MSTest”测试项目

global using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace _27interface.UnitTests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
        }
    }
}

测试前,需要为测试项目添加依赖项

实例

新建FakeShippingCalculator类 —— “假”的价格计算系统

using HelloWord;

namespace _27interface.UnitTests
{
    internal class FakeShippingCalculator : IShippingCalculator
    {
        public float CalculateShipping(Order order)
        {
            return 5;
        }
    }
}

测试用例Assert

using HelloWord;

namespace _27interface.UnitTests
{
    [TestClass]
    public class OrderProcessorTest
    {
        //被测条件_条件_期望结果
        [TestMethod]
        public void Process_OrderUnshipped_SetShippment()
        {
            OrderProcessor orderProcessor = new OrderProcessor(new FakeShippingCalculator());

            Order order = new Order
            {
                Id = 1,
                DatePlaced = DateTime.Now,
                TotalPrice = 0
            };

            orderProcessor.Process(order);

            //测试用例Assert
            Assert.AreEqual(order.Shipment.Cost,5);
            Assert.IsTrue(order.IsShipped);
        }

        [TestMethod]
        //指明抛出的异常类型
        [ExpectedException(typeof(InvalidOperationException))]
        public void Process_OrderUnshipped_ThrowException() {
            OrderProcessor orderProcessor = new OrderProcessor(new FakeShippingCalculator());

            Order order = new Order
            {
                Id = 145,
                DatePlaced = DateTime.Now,
                TotalPrice = 100f,
                IsShipped = true
            };

            orderProcessor.Process(order);
        }
    }
}

运行测试项目

反转控制与依赖注入

依赖注入

  • .NET的一等公民 —— 依赖注入
  • 依赖关系注入(DI- Dependencies Injection)是一种软件设计模式。
  • 在类及其依赖项之间实现控制反转(IoC)的技术。
  • 可以实现类似系统配置、日志记录和选项模式等功能模块的解耦。

IOC

  • Inversion of Control,控制反转、控制倒置。
  • 通过独立的接口串联系统。
  • IOC容器

IOC容器

依赖注入(DI)

  • 获得依赖对象的过程被反转了
  • 控制被反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入。

IOC与依赖注入

  • 从不同的角度的描述的同一件事情。
  • 通过引入IOC容器,实现对象之间的解耦。
  • 核心思想∶面向接口。

IOC的好处

  • 低耦合性
  • 标准性
  • 可重复性

实例 —— Nuget项目管理(依赖管理)

在NuGET中安装Microsoft.Extensions.DependencyInjection

配置IOC反转控制容器

using System;
using Microsoft.Extensions.DependencyInjection;

namespace HelloWord
{

    class Program
    {
        static void Main(string[] args)
        {
            var order = new Order
            {
                Id = 123,
                DatePlaced = DateTime.Now,
                TotalPrice = 100f
            };

/*            IShippingCalculator doubleElven = new DoubleElevenShippingCalculator();

            IShippingCalculator normal = new ShippingCalculator();
            var orderProcessor = new OrderProcessor(doubleElven);

            if (DateTime.Now != new DateTime(2050, 11, 11)) {
                orderProcessor = new OrderProcessor(normal);
            }

            orderProcessor.Process(order);*/

            //配置IOC 反转控制容器
            ServiceCollection services = new ServiceCollection();
            //singleton,单例模式
            //scoped,作用域模式
            //tansient,瞬时模式
            services.AddScoped<IOrderProcessor, OrderProcessor>();
            services.AddScoped<IShippingCalculator,DoubleElevenShippingCalculator>();

            Console.Read();
        }
    }
}

singleton,scoped,tansient(从IOC提取服务)(测试)

using System;
using Microsoft.Extensions.DependencyInjection;

namespace HelloWord
{

    class Program
    {
        static void Main(string[] args)
        {
            var order = new Order
            {
                Id = 123,
                DatePlaced = DateTime.Now,
                TotalPrice = 100f
            };


            //配置IOC 反转控制容器
            ServiceCollection services = new ServiceCollection();
            //singleton,单例模式
            //scoped,作用域模式
            //tansient,瞬时模式
            /*            services.AddSingleton<IOrderProcessor, OrderProcessor>();*/
/*            services.AddTransient<IOrderProcessor, OrderProcessor>();*/
            services.AddScoped<IOrderProcessor, OrderProcessor>();
            services.AddScoped<IShippingCalculator,DoubleElevenShippingCalculator>();

            //从IOC提取服务
            IServiceProvider serviceProvider = services.BuildServiceProvider(); 
            var orderProcess = serviceProvider.GetService<IOrderProcessor>();

            var orderProcess2 = serviceProvider.GetService<IOrderProcessor>();

            Console.Read();
        }
    }
}

总览

using System;
using Microsoft.Extensions.DependencyInjection;

namespace HelloWord
{

    class Program
    {
        static void Main(string[] args)
        {
            var order = new Order
            {
                Id = 123,
                DatePlaced = DateTime.Now,
                TotalPrice = 100f
            };


            //配置IOC 反转控制容器
            ServiceCollection services = new ServiceCollection();
            //singleton,单例模式
            //scoped,作用域模式
            //tansient,瞬时模式
            /*            services.AddSingleton<IOrderProcessor, OrderProcessor>();*/
/*            services.AddTransient<IOrderProcessor, OrderProcessor>();*/
            services.AddScoped<IOrderProcessor, OrderProcessor>();
            services.AddScoped<IShippingCalculator,DoubleElevenShippingCalculator>();

            //从IOC提取服务
            IServiceProvider serviceProvider = services.BuildServiceProvider(); 
            var orderProcess = serviceProvider.GetService<IOrderProcessor>();

            var orderProcess2 = serviceProvider.GetService<IOrderProcessor>();

            //处理订单
            orderProcess.Process(order);

            Console.Read();
        }
    }
}

多重继承vs多重实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _30Multiple_inheritance_vs.multiple_implementation
{
    public class UIText : UIBase, IDragable, ICopyable
    {
        public void Copy()
        {
            throw new NotImplementedException();
        }

        public void Drag()
        {
            throw new NotImplementedException();
        }

        public void Paste()
        {
            throw new NotImplementedException();
        }
    }

    public interface IDragable {
        void Drag();
    }

    public interface ICopyable
    {
        void Copy();
        void Paste();
    }
    public class UIBase { 
        public int Size { get; set; }
        public int Position { get; set; }

        public void Draw() {
            Console.WriteLine("绘制UI");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.Read();
        }
    }
}

接口与多态

OCP开闭原则

新建接口类 —— INotification.cs

namespace HelloWord
{
    public interface INotification
    {
        public void Send(string message);
    }
}

MailService实现INotification

using System;

namespace HelloWord
{
    internal class MailService : INotification
    {
        public void Send(string message)
        {
            Console.WriteLine("发送email",message);
        }
    }
}

新建接口类 —— SmsMessageService.cs

using System;
namespace HelloWord
{
    public class SmsMessageService : INotification
    {
        public void Send(string message)
        {
            Console.WriteLine("短信: " + message);
        }
    }
}

修改OrderProcessor

using System;
using System.Collections.Generic;

namespace HelloWord
{
    public class OrderProcessor
    {
/*        private readonly MailService _mailService;*/
        private readonly List<INotification> messageServices;

        public OrderProcessor()
        {
            messageServices= new List<INotification>();
        }

        public void RegisterNotification(INotification notification)
        {
            messageServices.Add(notification);
        }

        public void Process(Order order)
        {
            // 处理订单...处理发货...

            // 通知用户收货
            /*_mailService.Send("订单已发货");*/
            foreach (INotification n in messageServices) {
                n.Send("订单已发货");
            }
        }
    }
}

Program.cs 新增代码 实现服务

using System;
using System.Collections;
using System.Collections.Generic;

namespace HelloWord
{
    class Program
    {
        static void Main(string[] args)
        {
            var order = new Order
            {
                Id = 123,
                DatePlaced = DateTime.Now,
                TotalPrice = 30f
            };

            OrderProcessor orderProcessor = new OrderProcessor();

            //新增代码 _ 实现服务
            MailService mailService = new MailService();
            orderProcessor.RegisterNotification(mailService);

            INotification smsService = new SmsMessageService();
            orderProcessor.RegisterNotification(smsService);    

            orderProcessor.Process(order);
            
            Console.Read();
        }
    }
}

面向接口案例改造重构

User.cs

using System;

namespace CMS
{
    partial class Program
    {
        public class User : IUser
        {
            public bool IsUserLogin { get; set; }

            public void Login()
            {
                string username;
                string password;

                username = CmdReader("用户名: ");
                if (username != "alex")
                {
                    Console.WriteLine("查无此人");
                    return;
                }

                password = CmdReader("密码: ");
                if (password != "123456")
                {
                    Console.WriteLine("密码错误, 请重试。");
                    return;
                }

                IsUserLogin = true;
            }
        }
    }
}

IUser.cs接口

namespace CMS
{
    public interface IUser
    {
        bool IsUserLogin { get; set; }

        void Login();
    }
}

CMSController

namespace CMS
{
    partial class Program
    {
        public class CMSController : ICMSController
        {
            private readonly IUser _user;
            private readonly IMenu _menu;
            public CMSController(IUser user, IMenu menu)
            {
                _menu = menu;
                _user = user;
            }

            public void Start()
            {
                // login
                do
                {
                    _user.Login();
                } while (!_user.IsUserLogin);

                // start Menu
                _menu.ShowMenu();
            }

        }
    }
}

ICMSController.cs接口

namespace CMS
{
    public interface ICMSController
    {
        void Start();
    }
}

Menu.cs

using System;

namespace CMS
{
    partial class Program
    {
        public class Menu : IMenu
        {
            public void ShowMenu()
            {
                bool isExit = false;
                while (!isExit)
                {
                    string selection = CmdReader("主菜单\n1.客户管理\n2.预约管理\n3.系统设置\n4.退出\n请选择: ");
                    switch (selection)
                    {
                        case "1":
                            Console.WriteLine("客户管理");
                            break;
                        case "2":
                            Console.WriteLine("预约管理");
                            break;
                        case "3":
                            Console.WriteLine("系统设置");
                            break;
                        case "4":
                        default:
                            Console.WriteLine("退出");
                            isExit = true;
                            break;
                    }
                }
            }
        }
    }
}

IMenu接口

namespace CMS
{
    public interface IMenu
    {
        void ShowMenu();
    }
}

Program.cs

创建 IOC 反转控制容器

注册服务

创建BuildServiceProvider,提取cmsController实例

using System;
using Microsoft.Extensions.DependencyInjection;

namespace CMS
{
    partial class Program
    {
        public static string CmdReader(string instruction)
        {
            Console.Write(instruction);
            string cmd = Console.ReadLine();
            return cmd;
        }

        public static void Main(string[] args)
        {
            // 创建 IOC 反转控制容器
            var collection = new ServiceCollection();

            // 注册服务
            collection.AddScoped<IUser, User>();
            collection.AddScoped<IMenu, Menu>();
            collection.AddScoped<ICMSController, CMSController>();

            // 创建BuildServiceProvider,提取cmsController实例
            IServiceProvider serviceProvider = collection.BuildServiceProvider();
            var cmsController = serviceProvider.GetService<ICMSController>();

            cmsController.Start();

            Console.Read();
        }
    }
}

声明:三二一的一的二|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - C# - 面向对象 - 接口


三二一的一的二