自上而下的使用设计模式与自上而下的揭示迭代式开发和持续性重构结合起来。所以我们尽可能的去寻找使用模式改进代码的新可能。记录了具有公认良好属性的程序结构。然而,并非每一个设计模式都能完美的适用并应用于我们的项目。所以如果我们从最初的代码去了解每一个设计模式的针对具体情况不断演变而生的过程。获取我们能重新得到一种思路和一种共鸣,从而简化我们的项目。
现在我们有两个这样的类。SQLServerUser 调用BasicInfo 的信息。
后来又来了一个AccessUser 也要调用BasicInfo信息,而且它的方法跟SQLServer一样。
既然两个类中的方法都相同。 不得不想,以后还有没有可能有其他的类增加,导致继续重复。没错,在我们的需求里,以后还有可能换成OracleUser等。这就往往意味着又有了坏的味道。 重复可以进行抽象。
抽象的方法有很多种。使用接口,抽象类,或是虚类。普通/抽象类+虚方法等。
那么他们的区别:
从抽象的内容来看
- 接口只定义方法名,参数和返回值。所以它更像一种规范。
- 抽象类中可以有字段,可以存在普通方法(可以有方法体)。也存在抽象方法,只是抽象方法同样不能有方法体。
- 虚方法必须有方法体。子类可以重写。
从继承的情况来看
- 一个子类只能继承一个抽象类,而一个子类能继承多个不同的接口。
- 子类需要全部重写父类的接口,也需要全部重写父类的抽象类,却不需要全部重写父类的虚方法。
分析:
1. 由于多个数据库,有多个相同的方法,却没有多个相同实现(所以不是一定要用抽象类)。
2. 又由于子类只能继承一个抽象类,能继承多个接口。
综上:采用接口最佳。
于是有了下图。
这样我们的代码有了一定的改观。以后再扩展直接去实例化IDataBase 这个接口。再调用形式上也从
以前的 SQLServerUser squServerUser=new SQLServerUser();
改成了 IDataBase dataBase=new SQLServerUser();
其实这还是半解耦状态。暂且放一放。
需求:因为数据库还有多张表,必然还会有对应的多个类似的形式。
如此,我们就需要不断的去实例化。
IDataBase dataBase=new SQLServerUser();
Idepartment department=new SQLServerDepartment();
哪一天一旦说更改了Access数据库。我们就需要将遍布到程序所有蒋璐的所有这些实例化的地方全部改为Access相关类。
不如我们用一个统一的类把创建类管理起来。
这样我们只需要实例化一个SQLFactory 就有了SQL的一套类。这就是抽象工厂最初的模式了。然而我们来看左边的两个Factory 其方法都是一致的。这时候,我们还可以继续按照最初的思路对其进行抽象,接口 抽象类等,当然我也可以按照我们的简单工厂,通过case语句将其合并为一个类。这样DataAccess类中可以写为这样。
class DataAccess
{
private static readonly string db = "Sqlserver";
//private static readonly string db = "Access";
public static IUser CreateIDataBase()
{
IUser result = null;
switch (db)
{
case "Sqlserver":
result = new SqlserverUser();
break;
case "Access":
result = new AccessUser();
break;
}
return result;
}
public static IDepartment CreateDepartment() {
//............
}
}
还是那句话,没有最合适的设计模式,因为所有特定的设计模式都是为了应对特定的需求和变化。我们只能预测需求的变化方向来选择和调整合适的设计模式。Switch Case 就回到了最初的简单工厂模式。我们也知道当有新增加一个ORacle数据库时它是有不足的。 因为 它需要修改switch Case里面的内容。
这时候,我们又选择了反射机制+配置文件来让其自己“去某个地方找应该要实例化的类是哪一个”。
主要的是下面这几句话:
Private static readonly string db=ConfigurationManager.AppSettings["DB"];
Public static Iuser CreateUser(){
String className=AssemblyName+"."+db+"User";
Return (IUser)Assembly.Load(AssemblyName).CreateInstance(className);
}
总结
以前侧重于抽象工厂能够帮助我们解决问题。而本篇博客侧重于在某一种需求出来之后 它是怎么根据变化进行的调整。这往往能给做开发的我们提供一个更好的思路怎么去提高自己的代码逼格。当然,从中我们也感受到,优秀的设计并非一蹴而就,它们都经历的艰难-笨重-和反思。随着代码的不断改变,看到了更好的设计方案。
对于程序员来说,这种欣喜也许其它任何不可替代。