简介

在之前的简单工厂里面有两个问题,第一个是switch case的使用会使代码随着类的增多而增多,不便于后面的维护(试想你看着代码里面,一堆的case…),第二个问题是,工厂返回的是指定的类,返回指定类为什么不太友好呢?因为这就相当于,我需要知道你是谁才知道你能干什么(如果现在还不懂什么意思的话,不要着急哈,后面看了我代码的实现就知道了)。

假设有个项目经理

对,这次来假设,有个项目经理,找你做一个对SqlServer数据库进行读写的基本程序,然后你就想,项目经理这种比女朋友还多变的人啊,他叫我写一个用SqlServer的,可能我都还没写完,就跟我说要改成使用Mysql的数据库,那我不就又白写了。这个时候突然灵光一现,我不是才学完简单工厂模式,终于可以用上了,拍了几下胸口(虽然我也不知道为什么写代码要先拍几下胸口),几秒钟后,就写出来了。

public abstract class DBBase
{
    public abstract void Query();
}
public class SqlServerDB : DBBase
{
    public override void Query()
    {
        Console.WriteLine("从server中查询");
    }
}
public class MysqlDB : DBBase
{
    public override void Query()
    {
        Console.WriteLine("从Mysql查询");
    }
}
public class DBFactory
{
    public static DBBase CreateDB(string dbType)
    {
        switch (dbType)
        {
            case "SqlServer":
                return new SqlServerDB();
            case "Mysql":
                return new MysqlDB();
            default:
                throw new ArgumentException("Unsupported db type.");
        }
    }
}

 

这样写呢,的确是可以满足到项目经理的需求了,但是,这种做法还不够抽象,为什么呢?因为如果需要使用和实现Query这个方法的时候,必须先继承自DBBase,再重写实现,但是并不是所有类都能够或者说应该继承自DBBase这个类的,。

假设我现在有一个User类,想要它拥有这个Query的能力,我应该怎么写呢?

public class User: DBBase
{
    public override void Query()
    {
        Console.WriteLine("User实现查询功能");
    }
}

 

这里看着就已经很奇怪了,明明是一个User(用户)类,为什么它要去继承自DBBase(数据库基类,我打了半天的【鸡肋】,才把基类打出来…)呢,这个时候你可能会说,因为要实现Query这个方法啊,那这就有一种情况了,假如User本身已经继承自UserBase这个类,现在还是希望能实现Query这个方法怎么办,在C#中是不能有多个父类的(只能有父类、爷爷类、曾祖父类…一直到祖先类),你又会说,那我在UserBase里面加这个方法咯,这也是不对的,我们不能为了子类的实现而破坏父类。那怎么办呢,这个时候,接口(英文名: Interface)就该商量登场了, 而抽象工厂就是使用了接口来进行抽象的。

// 定义一个数据库接口
public interface IDBService
{
    void Query();
}
public class SqlServerDB : IDBService
{
    public void Query()
    {
        Console.WriteLine("从server中查询");
    }
}
public class MysqlDB : IDBService
{
    public void Query()
    {
        Console.WriteLine("从Mysql查询");
    }
}
public class DBFactory
{
    public static IDBService CreateDB(string dbType)
    {
        switch (dbType)
        {
            case "SqlServer":
                return new SqlServerDB();
            case "Mysql":
                return new MysqlDB();
            default:
                throw new ArgumentException("Unsupported db type.");
        }
    }
}

调用方式如下。

 

static void Main(string[] args)
{

    IDBService dbService = DBFactory.CreateDB("Mysql");
    dbService.Query();
}

 

上面的代码主要修改部分,就是两个数据库类从原来的继承自DBBase,修改为实现IDBService接口,而工厂的返回类型也修改为IDBService接口。这样看好像也没有什么太大区别啊?
其实是很大区别的,这个区别就是Class 和 Interface的区别了。Class定义了一类事物的属性和行为,比如人类,有手、头、身高、体重等等的属性(字段),也有写字、行走等的行为,它是一类事物的描述。而Interface定义的主要是行为(当然在C#里面也可以定义属性),比如行走、飞行、游泳,它的描述里面,是少了类这种性质的,比如我们看到人类,就知道他可以行走,看到鸟类,就知道它可以飞行(我不知道🐔算不算鸟,但好像它也会飞?),这个就是Class。但我们看到一样东西会行走,它不一定就是人类,会飞行的不一定就是鸟类,只知道会行走,会飞行这样的行为,而这个就是Interface。
这个就是我开篇说的,类是需要知道你是谁我才知道你能干什么,但接口就是我是不需要知道你是谁,我仅仅知道你能干什么。

上面讲了那么多,其实就已经说明了抽象工厂的优势了,第二个问题也就解决了,下面我们解决下第一个问题(为什么不是从第一个问题开始解决?因为第一个问题比较难嘛,哈哈哈哈)。

这里要解决掉switch case的办法,就是使用反射技术了(反射技术简直是可以为所欲为啊),这里就不吹什么是反射了,因为项目经理又不会理你用什么技术,哈哈哈哈。

其实很简单,我们只需要该一下DBFactory的静态方法就可以了。

public class DBFactory
{
    public static IDBService CreateDB(string dbType)
    {
        // Load方法的参数是对应类型所在的程序集
        // CreateInstance方法的参数是对应需要实例化的类型
        IDBService dbService = (IDBService) Assembly.Load("抽象工厂").CreateInstance($"抽象工厂.{dbType}");
        return dbService;
    }
}

调用方式如下。

static void Main(string[] args)
{
            // 这里传的就是需要使用的类型名
            IDBService dbService = DBFactory.CreateDB("SqlServerDB");
            dbService.Query();
}

 

这里可以看到我的程序集是中文命名的,其实这是不规范的哦,我只是为了容易标示我自己写的案例的代码,才用的中文的,实际使用中最好还是使用英文,避免做不规范的事情。

经过使用反射后,我们的抽象工厂,就可以正式和switch case分手了(不知道你们有没有强迫症,看到switch case 总想把它去掉- -)。
这里可以看到,使用反射的话,只需要传递一个类型名的字符串,就可以帮我们实例出对应的对象来,那既然是字符串,是不是就有一个大胆的想法呢?对,这个大胆的想法就是将这个值写到配置文件中,通过读取配置文件的设置来选择,我使用的到底是SqlServer还是Mysql的数据库,这样就可以做到,不需要修改源代码就可以更换,这样,遇到再多变的项目经理,也不怕加班了,要换?没关系,配置文件改一下,立刻下班,哈哈哈哈哈。

总结

这篇文章可能文字的描述要比代码多很多,主要是因为呢大部分都是一些概念性和理解性的问题。上面描述的也是编程中一种世界观的体现,毕竟编程是一门艺术嘛。
接口Interface真的很重要,像一些IOC容器,包括.net core3.1自带的DI,在注册服务的时候,都是注册的接口和实现类,基本没有去注册一个基类和子类的。还是那句话,因为使用接口,不需要知道你是谁,只需要知道你能干什么。
接下来的文章,我还会和大家分享编程这门艺术的,期待下一遍博客吧。

————————————————
版权声明:本文为CSDN博主「爱码星人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jmelzc/article/details/104683630/

原文地址:http://www.cnblogs.com/zt11402/p/16899512.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性