MySQL, Oracle, Linux, 软件架构及大数据技术知识分享平台

网站首页 > 数据库 / 正文

设计模式之工厂模式

2024-11-26 17:17 huorong 数据库 6 ℃ 0 评论

一、简单工厂

简单工厂其实不是一个设计模式,反而比较像一种编程习惯

1.1 定义

简单工厂又叫静态工厂方法,它提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类。

1.2 使用场景

  • 1.工厂类负责创建的对象比较少
  • 2.客户端(应用层)只知道出传入工厂类的参数,而对于如何创建对象(逻辑)不关心

1.3 优点&缺点

优点

只需要传入一个正确的参数,就可以获取你所需要的对象,而无须知道其创建细节。

缺点

工厂类的职责相对国过重,增加新的产品,需要修改工厂类的判断逻辑,违背开闭原则

1.4 代码演示

比如,上一篇文章中,OrderService类,使用到持久层MySqlDao、SqlServerDao。我们创建一个简单的持久层工厂。

public interface AbstractDao {
 /**
 * 插入订单
 */
 void insertOrder();
}

public class SqlServerDao implements AbstractDao{
 @Override
 public void insertOrder() {
 System.out.println("通过SqlServer插入数据");
 }
}

public class MySqlDao implements AbstractDao {
 @Override
 public void insertOrder() {
 System.out.println("通过MySql插入数据");
 }
}
public class DaoFactory {
 public static AbstractDao createDao(String type) {
 if ("mysql".equals(type)) {
 return new MySqlDao();
 } else if ("sqlserver".equals(type)) {
 return new SqlServerDao();
 }
 return null;
 }
}

系统内如果需要加入Oracle数据,这时候还是需要修改DaoFactory,这样违背了设计模式的开闭原则。修改下代码。如下:

public static AbstractDao createDao(Class className) {
 AbstractDao abstractDao = null;
 try {
 abstractDao = (AbstractDao) Class.forName(className.getName()).newInstance();
 } catch (Exception ex) {
 ex.printStackTrace();
 }
 return abstractDao;
}

1.6 java源码中简单工厂的应用

Calendar类的中createCalendar方法中

...
if (cal == null) {
 // If no known calendar type is explicitly specified,
 // perform the traditional way to create a Calendar:
 // create a BuddhistCalendar for th_TH locale,
 // a JapaneseImperialCalendar for ja_JP_JP locale, or
 // a GregorianCalendar for any other locales.
 // NOTE: The language, country and variant strings are interned.
 if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
 cal = new BuddhistCalendar(zone, aLocale);
 } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
 && aLocale.getCountry() == "JP") {
 cal = new JapaneseImperialCalendar(zone, aLocale);
 } else {
 cal = new GregorianCalendar(zone, aLocale);
 }
}
...

1.7 小结

简单工厂命名建议:类名称建议为“模块名称+Factory”。方法名通常为“get+接口名称”或者“create+接口名称”。

简单工厂的方法其实就是==选择==一个合适的实现类来使用,不是真正创建的。

由于客户端在调用工厂的时候传入了选择的参数,这就说明客户端必须知道每个参数的意义,也需要理解每个参数对应的功能处理。这就要求必须在一定程度上,向客户端暴露一定的内部实现细节。

二、工厂方法

工厂方法指的是作为工厂的方法

2.1 定义

定义一个创建对象的接口,但让实现这个接口的类来决定实例化哪个类,工厂方法让类的实例化推迟到子类中进行。

2.2 适用场景

  • 1.创建对象需要大量重复的代码
  • 2.客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 3.一个类通过其子类来指定创建哪个对象

2.3 优点&缺点

优点

用户只需要关心所需产品对应的工厂,无须关心创建细节。加入新的产品符合开闭原则,提高可扩展性。

缺点

类的个数容易过多,增加复杂度。增加了系统的抽象性和理解难度。

2.4 代码示范

继续上面的代码,原本DaoFactory负责了所有具体类的实例化。通过工厂方法,将DaoFactory定义成抽象或者接口,由其子类来决定实例化哪个。这样就将类的实例化推迟到子类。

public abstract class DaoFactory {
 public abstract AbstractDao createDao();
}

public class MysqlDaoFactory extends DaoFactory{
 @Override
 public AbstractDao createDao() {
 return new MySqlDao();
 }
}

public class SqlServerDaoFactory extends DaoFactory {
 @Override
 public AbstractDao createDao() {
 return new SqlServerDao();
 }
}

测试

public class OrderServerTests {
 public static void main(String[] args) {
 AbstractDao dao = new MysqlDaoFactory().createDao();
 OrderService orderService=new OrderService();
 orderService.setDao(dao);
 orderService.insertOrder();
 }
}

如果这时候需要增加Oracle数据库,这时候就增加OracleDaoFactory就行。

public class OracleDao implements AbstractDao {
 @Override
 public void insertOrder() {
 System.out.println("使用Oracle插入数据");
 }
}
public class OracleDaoFactory extends DaoFactory {
 @Override
 public AbstractDao createDao() {
 return new OracleDao();
 }
}

客户端只需要修改AbstractDao的实例指向OracleDaoFactory

public class OrderServerTests {
 public static void main(String[] args) {
 AbstractDao dao = new OracleDaoFactory().createDao();
 OrderService orderService=new OrderService();
 orderService.setDao(dao);
 orderService.insertOrder();
 }
}


2.5 简单工厂与工厂方法的区别

简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,对于客户端而言,去除了与具体产品的依赖。但是对于新增其他逻辑,则需要修改工厂类。从而违背了开闭原则。

工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在,也就是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码来进行,你想要加功能,本来就改工厂类的,而现在是修改客户端。

三、抽象工厂

3.1 定义

抽象工厂模式提供了一个创建==一系列==相关或者相互依赖对象的接口。无须指定它们具体的类型

3.2 使用场景

  • 1.客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
  • 2.强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码
  • 3.提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现

3.3 优点&缺点

优点

具体产品在应用层代码隔离,无须关心创建细节。将一个系列的产品族统一到一起创建

缺点

规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口

增加了系统的抽象性和理解难度

3.4 代码示范

同样使用上面的MySql、SqlServer持久层代码示例。原先我们在AbstractDao只定义了insertOrder方法,而实际的业务代码中,还有其他的数据库操作的方法,比如删除订单、更新订单、查询订单一系列操作。我们并不关心具体的比如插入订单方法,而关心整个持久层的所有的方法。

public interface InsertOrder {
 void insertOrder();
}

public interface QueryOrder {
 void queryOrder();
}

public class MySqlInsertOrder implements InsertOrder {
 @Override
 public void insertOrder() {
 System.out.println("使用Mysql插入订单");
 }
}

public class MySqlQueryOrder implements QueryOrder {
 @Override
 public void queryOrder() {
 System.out.println("使用Mysql查询订单");
 }
}

public class SqlServerInsertOrder implements InsertOrder {
 @Override
 public void insertOrder() {
 System.out.println("使用SqlServer插入订单");
 }
}

public class SqlServerQueryOrder implements QueryOrder {
 @Override
 public void queryOrder() {
 System.out.println("使用sqlServer查询订单");
 }
}

抽象工厂

public abstract class DaoFactory {
 public abstract InsertOrder getInsertOrder();

 public abstract QueryOrder getQueryOrder();
}


public class MysqlDaoFactory extends DaoFactory {
 @Override
 public InsertOrder getInsertOrder() {
 return new MySqlInsertOrder();
 }

 @Override
 public QueryOrder getQueryOrder() {
 return new MySqlQueryOrder();
 }
}

public class SqlServerDaoFactory extends DaoFactory {
 @Override
 public InsertOrder getInsertOrder() {
 return new SqlServerInsertOrder();
 }

 @Override
 public QueryOrder getQueryOrder() {
 return new SqlServerQueryOrder();
 }
}

测试

public class Test {
 public static void main(String[] args) {
 DaoFactory daoFactory = new MysqlDaoFactory();
 InsertOrder insertOrder = daoFactory.getInsertOrder();
 QueryOrder queryOrder = daoFactory.getQueryOrder();
 insertOrder.insertOrder();
 queryOrder.queryOrder();
 }
}


3.5 工厂方法与抽象工厂区别

工厂方法模式一般是针对单独的产品对象创建,而抽象工厂模式注重产品簇对象的创建,这就是它们之间的区别。如果产品簇只有一个产品,这时候抽象方法跟工厂方法是差不多的。

Tags:oracle insert as

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言