15.3用了工厂方法模式的数据访问程序
代码结构图 IUser接口,用于客户端访问,解除与具体数据库访问的耦合。
interface IUser
{
public function Insert($user);
public function GetUser($id);
}
SqlServerUser类,用于访问SQL Server的User。
class SqlServerUser implements IUser
{
public function Insert($user)
{
echo '在SQL Server中给User表增加一条记录' . PHP_EOL;
}
public function GetUser($id)
{
echo '在SQL Server中根据ID得到User表一条记录' . PHP_EOL;
}
}
AccessUser类,用于访问Access的User。
class AccessUser implements IUser
{
public function Insert($user)
{
echo '在Access中给User表增加一条记录' . PHP_EOL;
}
public function GetUser($id)
{
echo '在Access中根据ID得到User表一条记录' . PHP_EOL;
}
}
IFactory接口,定义一个创建访问User表对象的抽象的工厂接口。
interface IFactory
{
public function CreateUser();
}
SqlServerFactory类,实现IFactory接口,实例化SqlServerUser。
class SqlServerFactory implements IFactory
{
public function CreateUser()
{
return new SqlServerUser();
}
}
AccessFactory类,实现IFactory接口,实例化AccessUser。
class AccessFactory implements IFactory
{
public function CreateUser()
{
return new AccessUser();
}
}
客户端代码
public function factoryMethodImp()
{
$user = new User();
//若要改成Access数据库,只需要将本句
//改成$factory = new AccessFactory();
$factory = new SqlServerFactory();
$iu = $factory->CreateUser();
$iu->Insert($user);
$iu->GetUser(1);
}
15.4用了抽象工厂模式的数据访问程序
代码结构图 IDepartment接口,用于客户端访问,解除与具体数据库访问的耦合。
interface IDepartment
{
public function Insert($department);
public function GetDepartment($id);
}
SqlServerDepartment类,用于访问SQL Server的Department。
class SqlServerDepartment implements IDepartment
{
public function Insert($dept)
{
echo '在SQL Server中给Department表增加一条记录' . PHP_EOL;
}
public function GetDepartment($id)
{
echo '在SQL Server中根据ID得到Department表一条记录' . PHP_EOL;
}
}
AccessDepartment类,用于访问Access的Department。
class AccessDepartment implements IDepartment
{
public function Insert($dept)
{
echo '在Access中给Department表增加一条记录' . PHP_EOL;
}
public function GetDepartment($id)
{
echo '在Access中根据ID得到Department表一条记录' . PHP_EOL;
}
}
IFactory接口,定义一个创建访问Department表对象的抽象的工厂接口。
interface IFactory
{
public function CreateUser();
//增加的接口方法
public function CreateDepartment();
}
SqlServerFactory类,实现IFactory接口,实例化SqlServerUser和SqlServerDepartment。
class SqlServerFactory implements IFactory
{
public function CreateUser()
{
return new SqlServerUser();
}
//增加了SqlServerDepartment工厂
public function CreateDepartment()
{
return new SqlServerDepartment();
}
}
AccessFactory类,实现IFactory接口,实例化AccessUser和AccessDepartment。
class AccessFactory implements IFactory
{
public function CreateUser()
{
return new AccessUser();
}
//增加了AccessDepartment工厂
public function CreateDepartment()
{
return new AccessDepartment();
}
}
客户端代码
public function abstractFactoryImp()
{
$user = new User();
$dept = new Department();
//只需确定实例化哪一个数据库访问对象给factory
//$factory = new SqlServerFactory();
$factory = new AccessFactory();
//则此时已与具体的数据库访问解除了依赖
$iu = $factory->CreateUser();
$iu->Insert($user);
$iu->GetUser(1);
//则此时已与具体的数据库访问解除了依赖
$id = $factory->CreateDepartment();
$id->Insert($dept);
$id->GetDepartment(1);
}
显示结果如下
在Access中给User表增加一条记录
在Access中根据ID得到User表一条记录
在Access中给Department表增加一条记录
在Access中根据ID得到Department表一条记录
15.5抽象工厂模式
抽象工厂模式(AbstractFactory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂模式(AbstractFactory)结构图
AbstractProductA 和 AbstractProductB 是两个抽象产品,之所以为抽象,是因为它们都有可能有两种不同的实现,就刚才的例子来说就是 User 和 Department,而ProductA1、ProductA2 和 ProductB1、ProductB2就是对两个抽象产品的具体分类的实现,比如 ProductA1 可以理解为是 SqlServerUser,而 ProductB1 是 SqlServerDepartment。
IFactory是一个抽象工厂接口,它里面应该包含所有的产品创建的抽象方法。而ConcreteFactory1和ConcreteFactory2就是具体工厂了。就像SqlServerFactory和AccessFactory一样。
通常是在运行时刻再创建一个ConcreteFactory类的实例,这个具体的工厂再创建具有特定实现的产品对象,也就是说,为创建不同的产品对象,客户端应使用不同的具体工厂。
15.6抽象工厂模式的优点与缺点
抽象工厂模式的优点:
- 易于交换产品系列,由于具体工厂类,在一个应用中只需要在初始化的时候出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置。
- 它让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。
抽象工厂模式的缺点:
比如增加项目表Project,至少要增加三个类,IProject、SqlServerProject、AccessProject,还需要更改IFactory、SqlServerFactory和AccessFactory才可以完全实现。
编程是门艺术,这样大批量的改动,显然是非常丑陋的做法。
15.7用简单工厂来改进抽象工厂
代码结构图 去除IFactory、SqlServerFactory和AccessFactory三个工厂类,取而代之的是DataAccess类,代码如下
class DataAccess
{
private static $db = 'SqlServer';
// private static $db = 'Access';
public static function CreateUser()
{
$result = null;
switch (self::$db) {
case 'SqlServer':
$result = new SqlServerUser();
break;
case 'Access':
$result = new AccessUser();
break;
}
return $result;
}
public static function CreateDepartment()
{
$result = null;
switch (self::$db) {
case 'SqlServer':
$result = new SqlServerDepartment();
break;
case 'Access':
$result = new AccessDepartment();
break;
}
return $result;
}
}
客户端代码
public function ImproveBySimpleFactory()
{
$user = new User();
$dept = new Department();
$iu = DataAccess::CreateUser();
$iu->Insert($user);
$iu->GetUser(1);
$id = DataAccess::CreateDepartment();
$id->Insert($dept);
$id->GetDepartment(1);
}
客户端没有出现任何一个SQL Server 或 Acces 的字样,达到了解耦的目的。
15.8用反射+抽象工厂的数据访问程序
DataAccess类,用反射技术,取代IFactory、SqlServerFactory 和 AccessFactory。
class DataAccess
{
private static $namespace = 'app\design\logic\AbstractFactory\ImproveByReflection\\';
private static $db = 'SqlServer';
// private static $db = 'Access';
public static function CreateUser()
{
$className = self::$namespace . self::$db . 'User';
$ref_class = new \ReflectionClass($className);
return $ref_class->newInstance();
}
public static function CreateDepartment()
{
$className = self::$namespace . self::$db . 'Department';
$ref_class = new \ReflectionClass($className);
return $ref_class->newInstance();
}
}
15.9用反射+配置文件实现数据访问程序
通过增加配置项,来解决更改DataAccess的问题。
所有在用简单工厂的地方,都可以考虑用反射技术来去除 switch 或 if,解除分支判断带来的耦合。