自己能懂的设计模式-简单工厂模式
简单工厂模式
造房子时需要一个门,你是穿上木匠开始在你家们口锯木头,搞得一团糟,还是从工厂里生产一个
简言之,简单工厂为用户提供了一个实例,而隐藏了具体的实例化逻辑
简单工厂不是一个标准的设计模式,但是它实在是太常用了,简单而又神奇
简单工厂使用时机
当你创建一个对象,并非简单拷贝赋值,牵扯到很多其他逻辑时,就应该把它放到一个专门的工厂中,而不是每次都重复。
建议在以下情况中选用简单工厂
- 如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体,那么可以选用简单工厂,让客户端通过工厂来获取响应的接口,而无须关心具体的实现
- 如果想要把对外创建对象的职责集中管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的、不相关的对象,可以把对外创建对象的职责集中到一个简单工厂来,从而实现集中管理和控制。
简单工厂的实现
C++实现
#include <iostream>
class IDoor {
public:
virtual float GetWidth() = 0;
virtual float GetHeight() = 0;
};
class WoodenDoor : public IDoor {
public:
WoodenDoor(float width, float height): m_width(width), m_height(height){}
float GetWidth() override { return m_width; }
float GetHeight() override { return m_height; }
protected:
float m_width;
float m_height;
};
class DoorFactory {
public:
static IDoor* MakeDoor(float width, float heigh)
{
return new WoodenDoor(width, heigh);
}
};
int main()
{
IDoor* door = DoorFactory::MakeDoor(100, 200);
std::cout << "Width: " << door->GetWidth() << std::endl;
std::cout << "Height: " << door->GetHeight() << std::endl;
}
结果显示
g++ simple-factory.cpp -o simple-factory -std=c++11
./simple-factory
width: 100
height: 200
总结
C++版本的实现主要包括一个虚基类,实现虚基类的派生类以及构造派生类对象的工厂类
Golang实现
simple-factory.go
package simplefactory
import "fmt"
//iface is interface
type Iface interface {
Say(name string) string
}
//hiIface is one of Iface implement
type hiIface struct{}
//say hi to name
func (*hiIface)Say(name string) string {
return fmt.Sprintf("hi, %s", name)
}
//helloIface is another Iface implement
type helloIface struct{}
//say hello to name
func (*helloIface)Say(name string) string {
return fmt.Sprintf("hello, %s", name)
}
//NewIface return Iface interface by type
func NewIface(t int) Iface {
switch t {
case 1:
return &hiIface{}
case 2:
return &helloIface{}
default:
return nil
}
}
测试用例simple-factory_test.go
package simplefactory
import "testing"
//TestType1 test get hiIface with factory
func TestType1(t *testing.T) {
iface := NewIface(1)
s := iface.Say("rimond")
if s != "hi, rimond" {
t.Fatal("Type1 test failed")
}
}
//TestType2 test get helloIface with factory
func TestType2(t *testing.T) {
iface := NewIface(2)
s := iface.Say("liming")
if s != "hello, liming" {
t.Fatal("Type2 test failed")
}
}
结果
go test -v simple-factory.go simple-factory_test.go
=== RUN TestType1
--- PASS: TestType1 (0.00s)
=== RUN TestType2
--- PASS: TestType2 (0.00s)
PASS
ok command-line-arguments 0.002s
实现思路分析
因为golang没有继承的概念,因此通过接口实现来模拟继承操作,并通过暴露的工厂函数,根据传入的构造类型完成具体实现类型的创建
简单工厂的优缺点
简单工厂有以下优点:
- 帮助封装
简单工厂虽然简单,但是非常友好的帮助我们实现了组件的封装,然后让组件外部能真正面向接口编程 - 解耦
通过简单工厂,实现了客户端和具体实现类的解耦,如同上面的例子,客户端根本就不知道具体是由谁来实现,也不知道具体是如何实现的,客户端只是通过工厂获取它需要的接口对象。
简单工厂有以下缺点:
- 可能增加客户端的复杂度
如果通过客户端的参数来选择具体的实现类,那么就必须让客户端能理解各个参数所代表的具体功能和含义,这样会增加客户端使用的难度,也部分暴露了内部实现,这种情况可以选用可配置的方式来实现。 - 不方便扩展子工厂
通过type来区分同样不够灵活。
与其他相关模式的比较
抽象工厂模式 | 工厂方法模式 | 创建对象实例的模式 | |
---|---|---|---|
简单工厂模式 | 简单工厂用来选择实现,可以选择任意接口的实现,一个简单工厂可以有多个用于选择并创建对象的方法,多个方法创建的对象可以有关系也可以没有关系,抽象工厂模式是用来选择产品簇的实现的,也就是说一般抽象工厂里面有多个用于选择并创建对象的方法,但是这些方法所创建的对象之间通常是有关系的,这些被创建的对象通常是构成一个产品簇所需要的部件对象 | 工厂方法的本质也是用来选择实现的,跟简单工厂的区别在于工厂方法是把选择具体实现的功能延迟到子类去实现,如果把工厂方法选择的实现放到父类直接实现,那就等同于简单工厂 | 简单工厂的本质是选择实现,所以它可以跟其他任何能够具体的创建对象实例的模式配合使用,比如:单例模式、原型模式、生成器模式等 |