(点击
上方蓝字
,快速关注我们)
英文:kamranahmedse,编译:
伯乐在线 - Justin_YGG
如有好文章
投稿,请点击 → 这里了解详情
简介
设计模式用于解决反复出现的问题,是解决特定问题的指导方针。设计模式不是在应用中引用的类、package 或者库,而是在某些特定场景下解决特定问题的指导方针。
设计模式用于解决反复出现的问题,是解决某些特定问题的指导方针。
维基百科中这样描述设计模式:
在软件工程中,设计模式是针对软件设计中普遍存在(反复出现)的各种问题,所提出的可复用型解决方案。设计模式并不直接完成代码的编写,而是描述在不同情况下如何解决问题。
注意
另注:下面的示例代码是用 PHP7 实现的,因为概念是一样的,所以语言并不会阻碍你理解设计模式。其他语言版本的实现正在进行中。
设计模式分类
创建型模式
创建型模式专注于如何初始化对象 。
在软件工程中,创建型模式是处理对象创建的设计模式,试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题,或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决这些问题。
-
简单工厂模式
-
工厂方法模式
-
抽象工厂模式
-
生成器模式
-
原型模式
-
单例模式
🏠 简单工厂模式
想象一下,你正在建造一座房子而且需要几扇房门,如果每次需要房门的时候,不是用工厂制造的房门,而是穿上木匠服,然后开始自己制造房门,将会搞得一团糟。
简单工厂模式只是为客户端创建实例,而不将任何实例化逻辑暴露给客户端。
在面向对象程序设计中,工厂通常是一个用来创建其他对象的对象。通常来讲,工厂是指某个功能或方法,此功能或方法返回不同类型的对象或者类的某个方法调用,返回的东西看起来是「新的」。
首先是房门的接口和实现
interface
Door
{
public
function
getWidth
()
:
float
;
public
function
getHeight
()
:
float
;
}
class
WoodenDoor
implements
Door
{
protected
$width
;
protected
$height
;
public
function
__construct
(
float
$width
,
float
$height
)
{
$this
->
width
=
$width
;
$this
->
height
=
$height
;
}
public
function
getWidth
()
:
float
{
return
$this
->
width
;
}
public
function
getHeight
()
:
float
{
return
$this
->
height
;
}
}
然后是生产房门的工厂
class
DoorFactory
{
public
static
function
makeDoor
($
width
,
$
height
)
:
Door
{
return
new
WoodenDoor
($
width
,
$
height
);
}
}
$
door
=
DoorFactory
::
makeDoor
(
100
,
200
);
echo
'Width: '
.
$
door
->
getWidth
();
echo
'Height: '
.
$
door
->
getHeight
();
如果创建对象不仅仅是一些变量的初始化,还涉及某些逻辑,那么将其封装到一个专用工厂中取代随处使用的重复代码是有意义的。
🏭 工厂方法模式
考虑招聘经理的情况。一个人不可能应付所有职位的面试,对于空缺职位,招聘经理必须委派不同的人去面试。
工厂方法模式提供了一种将实例化逻辑委托给子类的方法。
在基于类的编程中,工厂方法模式是一种使用了工厂方法的创建型设计模式,在不指定对象具体类型的情况下,处理创建对象的问题。创建对象不是通过调用构造器而是通过调用工厂方法(在接口中指定工厂方法并在子类中实现或者在基类中实现,随意在派生类中重写)来完成。
以上述招聘经理为例,首先给出一个面试官接口及实现
interface
Interviewer
{
public
function
askQuestions
();
}
class
Developer
implements
Interviewer
{
public
function
askQuestions
()
{
echo
'Asking about design patterns!'
;
}
}
class
CommunityExecutive
implements
Interviewer
{
public
function
askQuestions
()
{
echo
'Asking about community building'
;
}
}
然后创建 HiringManager
abstract
class
HiringManager
{
// Factory method
abstract
public
function
makeInterviewer
()
:
Interviewer
;
public
function
takeInterview
()
{
$
interviewer
=
$
this
->
makeInterviewer
();
$
interviewer
->
askQuestions
();
}
}
现在,任何子类都可以继承 HiringManager 并委派相应的面试官
class
DevelopmentManager
extends
HiringManager
{
public
function
makeInterviewer
()
:
Interviewer
{
return
new
Developer
();
}
}
class
MarketingManager
extends
HiringManager
{
public
function
makeInterviewer
()
:
Interviewer
{
return
new
CommunityExecutive
();
}
}
$
devManager
=
new
DevelopmentManager
();
$
devManager
->
takeInterview
();
// Output: Asking about design patterns
$
marketingManager
=
new
MarketingManager
();
$
marketingManager
->
takeInterview
();
// Output: Asking about community building.
类中的一些常见处理需要在运行时动态决定所需的子类,换句话说,当客户端不知道可能需要的确切子类时,使用工厂方法模式。
🔨 抽象工厂模式
扩展一下简单工厂模式中的房门例子。基于所需,你可能需要从木门店获取木门,从铁门店获取铁门或者从相关的门店获取 PVC 门。进一步讲,你可能需要不同种类的专家来安装房门,比如木匠安装木门,焊接工安装铁门等等。正如你所料,房门有了依赖,木门需要木匠,铁门需要焊接工。
一组工厂的工厂:将相关或者互相依赖的单个工厂聚集在一起,而不指定这些工厂的具体类。
抽象工厂模式提供了一种方式,这种方式可以封装一组具有共同主题的个体工厂,而不指定这些工厂的具体类。
以上述房门为例,首先给出 Door 接口和一些实现
interface
Door
{
public
function
getDescription
();
}
class
WoodenDoor
implements
Door
{
public
function
getDescription
()
{
echo
'I am a wooden door'
;
}
}
class
IronDoor
implements
Door
{
public
function
getDescription
()
{
echo
'I am an iron door'
;
}
}
然后根据每种房门类型给出对应的安装专家
interface
DoorFittingExpert
{
public
function
getDescription
();
}
class
Welder
implements
DoorFittingExpert
{
public
function
getDescription
()
{
echo
'I can only fit iron doors'
;
}
}
class
Carpenter
implements
DoorFittingExpert
{
public
function
getDescription
()
{
echo
'I can only fit wooden doors'
;
}
}
现在抽象工厂可以将相关的对象组建在一起,也就是说,木门工厂会生成木门并提供木门安装专家,铁门工厂会生产铁门并提供铁门安装专家。
interface
DoorFactory
{
public
function
makeDoor
()
:
Door
;
public
function
makeFittingExpert
()
:
DoorFittingExpert
;
}
// Wooden factory to return carpenter and wooden door
class
WoodenDoorFactory
implements
DoorFactory
{
public
function
makeDoor
()
:
Door
{
return
new
WoodenDoor
();
}
public
function
makeFittingExpert
()
:
DoorFittingExpert
{
return
new
Carpenter
();
}
}
// Iron door factory to get iron door and the relevant fitting expert
class
IronDoorFactory
implements
DoorFactory
{
public
function
makeDoor
()
:
Door
{
return
new
IronDoor
();
}
public
function
makeFittingExpert
()
:
DoorFittingExpert
{
return
new
Welder
();
}
}
$
woodenFactory
=
new
WoodenDoorFactory
();
$
door
=
$
woodenFactory
->
makeDoor
();
$
expert
=
$
woodenFactory
->
makeFittingExpert
();
$
door
->
getDescription
();
// Output: I am a wooden door
$
expert
->
getDescription
();
// Output: I can only fit wooden doors
// Same for Iron Factory
$
ironFactory
=
new
IronDoorFactory
();
$
door
=
$
ironFactory
->
makeDoor
();
$
expert
=
$
ironFactory
->
makeFittingExpert
();
$
door
->
getDescription
();
// Output: I am an iron door
$
expert
->
getDescription
();
// Output: I can only fit iron doors
正如你看到的,木门工厂将木匠和木门封装在一起,同样地,铁门工厂将铁门和焊接工封装在一起。这样就可以帮助我们确保,对于每一扇生产出来的门,都能搭配正确的安装工。
当存在相关的依赖并涉及到稍复杂的创建逻辑时,使用抽象工厂模式。
👷 生成器模式
想象一下你在 Hardee’s 餐厅点了某个套餐,比如「大 Hardee 套餐」,然后工作人员会正常出餐,这是简单工厂模式。但是在很多情况下,创建逻辑可能涉及到更多步骤。比如,你想要一个定制的 Subway 套餐,对于你的汉堡如何制作有几个选项可供选择,比如你想要什么类型的酱汁?你想要什么奶酪? 在这种情况下,建造者模式便可以派上用场。
允许创建不同风格的对象,同时避免构造器污染。当创建多种风格的对象时或者创建对象时涉及很多步骤,可以使用生成器模式。
生成器模式是一种对象创建软件设计模式,其目的是找到重叠构造器反面模式的解决方案。
既然提到了,那我就补充一下什么是重叠构造器反面模式。 我们时不时地会看到如下构造函数:
public
function
__construct
($
size
,
$
cheese
=
true
,
$
pepperoni
=
true
,
$
tomato
=
false
,
$
lettuce
=
true
)
{
}
正如你看到的,构造器参数的数量可能会迅速失控,并且参数的排列可能让人难以理解。 如果将来要添加更多选项,此参数列表可能会不断增长,这被称为重叠构造器反面模式。
程序示例
理想之选是使用生成器模式,首先给出汉堡类
class
Burger
{
protected
$
size
;
protected
$
cheese
=
false
;
protected
$
pepperoni
=
false
;
protected
$
lettuce
=
false
;
protected
$
tomato
=
false
;
public
function
__construct
(
BurgerBuilder
$
builder
)
{
$
this
->
size
=
$
builder
->
size
;
$
this
->
cheese
=
$
builder
->
cheese
;
$
this
->
pepperoni
=
$
builder
->
pepperoni
;
$
this
->
lettuce
=
$
builder
->
lettuce
;
$
this
->
tomato
=
$
builder
->
tomato
;
}
}
然后是 builder
class
BurgerBuilder
{
public
$
size
;
public
$
cheese
=
false
;
public
$
pepperoni
=
false
;
public
$
lettuce
=
false
;
public
$
tomato
=
false
;
public
function
__construct
(
int
$
size
)
{
$
this
->
size
=
$
size
;
}
public
function
addPepperoni
()
{
$
this
->
pepperoni
=
true
;
return
$
this
;
}
public
function
addLettuce
()
{
$
this
->
lettuce
=
true
;
return
$
this
;
}
public
function
addCheese
()
{
$
this
->
cheese
=
true
;
return
$
this
;
}
public
function
addTomato
()
{
$
this
->
tomato
=
true
;
return
$
this
;
}
public
function
build
()
:
Burger
{
return
new
Burger
($
this
);
}
}
$
burger
=
(
new
BurgerBuilder
(
14
))