本节书摘来自异步社区《.NET程序员面试秘笈》一书中的第1章,面试题11,作者: 张云翯, 更多章节内容可以访问云栖社区“异步社区”公众号查看。
面试题11 举例说明简单工厂模式的作用
.NET程序员面试秘笈
【考点】工厂模式的理解,工厂模式在实际应用中的编写。
【出现频率】
【解答】
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时由于需求的变化,往往存在着更多系列对象的创建工作。为了绕过常规对象的创建方法(new运算符创建实例),工厂模式提供一种“封装机制”来减少使用程序和这种“多系列具体对象创建工作”的耦合性。
说明:
这里的程序指客户程序之类的使用者。
简单工厂模式可以用于封装创建实例的实现部分,在应用接口的程序中被广泛使用,其应用模型如图1.16所示。
为了处理更加复杂的情况,可以将图中的产品进行再次细分为多个大类,用抽象类进行归纳,完成同大类产品共用代码的复用。然后将工厂类也相应地分为多个大类,用抽象类进行归纳。将图1.16改良后如图1.17所示。
说明:
本图假设将A产品和B产品作为两大类产品(即将看作产品的具体实现类再次细分),每大类产品有两个产品,如A产品有A1和A2。
为了说明工厂模式在应用程序中的具体表现,在ch01目录下新建一个程序文件,并命名为Factory.cs,编写代码如程序1.12所示。
在命令行下编译Factory.cs后,执行Factory程序,其效果如图1.18所示。
本例声明了多个类,代码略显复杂,但是只要理解了图1.17,其实也容易掌握。在代码中,首先声明了一个接口,即Ihuman,其中含有两个未实现的方法。实现接口的类是两个抽象类,即Children(孩子)类和Adult(成年人)类,这两个类分别归纳了Boy类和Girl类,以及Man和Woman类。接口的getstatus方法成员在抽象类中实现,而getfav方法则映射为抽象方法,被抽象类的派生类实现。最后通过HumanFactory抽象类的两个派生类,根据不同的参数传递,创建不同的实例引用,并返回接口类型。
在主程序中,分别创建Factory1类和Factory2类的实例(f1和f2),并调用其gethuman方法。根据用户的输入决定创建哪个类的实例引用,并返回一个接口类型引用变量(h1和h2)。接口类型的引用变量调用两个方法时,使用者无法知道方法如何实现、由谁来实现。
【分析】
前面讲解接口的时候,着重分析了Bridge模式,接口可以简单地完成意图与实现的分离,以实现Bridge模式。由于类可以实现多个接口,所以类可以通过多个接口向外界提供多组不同的功能。接口反映了面向对象编程的特征之一,即多态,多态指通过相同方法得到不同的表现。接口也反映了面向对象编程的另一个特征,即封装,使用者并不清楚接口成员实现的细节,如以下代码所示:
interface Ibook //声明接口Ibook
{
void read(); //声明未实现的read方法
}
class BookA : Ibook
{
public void read() //实现read方法
{
Console.WriteLine("你在看A书。");
}
}
class BookB : Ibook
{
public void read() //实现read方法
{
Console.WriteLine("你在看B书。");
}
}
//使用接口的程序代码部分:
Ibook abc = new BookB(); //创建Ibook类的BookB实例引用
abc.read(); //调用read方法
接口类型的abc可以引用不同类的实例,以致相同的read方法可以有不同表现。但是以上代码的程序部分中,使用者仍然需要用new运算符进行相应的实例化,同时,使用者还是知道read方法由哪个派生类实现。为了进一步分离意图和实现,并对实现部分更好地封装,简单工厂模式可以进行一定的改良,添加代码如下所示:
class Factory
{
private Ibook _bka = new BookA();
private Ibook _bkb = new BookB();
public Ibook getbook(int i)
{
switch (i)
{
case 1:
return _bka;
case 2:
return _bkb;
default:
return _bka;
}
}
}
//使用接口的程序代码部分修改如下:
Factory f = new Factory();
Ibook abc = f.getbook(2); //调用f的getbook方法,并传递参数2
abc.read(); //调用read方法
Factory类封装了将接口各个派生类实例化的代码,这样,使用者只需要创建Factory类的实例,并调用getbook方法即可。向getbook方法传递不同的整数类型参数,可以创建不同的实例引用,而这些实例都是Ibook接口类型。如本例中,传递1将创建BookA类的实例引用(Ibook接口类型),传递2将创建BookB类的实例引用(Ibook接口类型)。使用者只知道传递数字来使用接口提供的不同功能,对内部实现却一无所知。Factory类则用于实例化各种类,相当于生产产品的工厂,其产品供接口类型的实例引用,这也是称其为工厂模式的原因。
说明:
本例中工厂类的getbook方法使用switch条件分支判断,然后返回相应的实例引用,在实例种类很多的情况下不大适用。根据具体情况不同,可以考虑利用反射、泛型等方法进行改进。
本文仅用于学习和交流目的,不代表异步社区观点。非商业转载请注明作译者、出处,并保留本文的原始链接。