本文来自李明子csdn博客(http://blog.csdn.net/free1985),商业转载请联系博主获得授权,非商业转载请注明出处!
摘要:本文讲解了工厂方法(Factory Method)设计模式的使用目的、基本形态及各参与者,并结合示例代码,讲解了该设计模式在具体业务场景下的使用。
工厂方法(Factory Method)提供了创建对象的方法,使对象的实例化延迟至子类。
工厂方法的基本形态如类图2-1所示。
结合图2-1,下面介绍各类在工厂方法设计模式中扮演的角色。
3.1 Product
Product是产品父类。虽然它可以是抽象类,也可以是接口,但一般情况下,它是包含各方法默认实现的普通类。
3.2 ConcreteProduct1
ConcreteProduct1是具体产品类。ConcreteProduct1类派生自Product类,可以重写Product中各方法,也可以对Product进行扩展。
3.3 Creator
Creator是创建者父类。它包含创建Product对象的工厂方法FactoryMethod。一般来说,Creator提供了工厂方法FactoryMethod的默认实现,创建Product的实例。另外,Creator也提供了一系列对Product操作的方法,这些方法大多数对Product及其子类是通用的,即不会在ConcreteCreator中被重写。
3.4 ConcreteCreator
ConcreteCreator派生自Creator,是具体产品ConcreteProduct1的创建者。该类重写了工厂方法FactoryMethod,创建ConcreteProduct1对象。另外,根据实际情况,ConcreteCreator也可能重写Creator的部分方法,以实现对ConcreteProduct1的个性化操作。
3.5 Client
Client是工厂方法设计模式的使用者,在上面的类图中并未体现。虽然实例化的具体产品类型对Client是透明的,但Client是知道产品的创建者所属的具体类并负责实例化该类的。
下面我们用一个业务场景实例来进一步讲解工厂方法的使用。
4.1 场景介绍
某电商平台将用户分为普通用户和VIP用户,不同种类的用户享受的折扣率不同,显示的用户信息栏目也不同。用户信息查看器用于输出用户的信息。
以下各节将介绍该场景各类的具体实现及其在工厂方法设计模式中所对应的参与者角色。
4.2 UserInfo
UserInfo是用户信息类,声明了用户名、用户生日、用户享受的折扣率等用户信息,并提供了用户信息对象的序列化方法。对应于工厂方法模式的参与者,UserInfo是我们的产品父类。下面的代码给出了UserInfo的声明。
package demo.designpattern.factorymethod;
import java.util.Date;
/**
* 用户信息
* Created by LiMingzi on 2017/5/9.
*/
public class UserInfo {
/**
* 用户名
*/
public String name;
/**
* 生日
*/
public Date birthday;
/**
* 折扣率
*/
public float discountRate = 0;
/**
* 构造方法
* @param name 用户名
* @param birthday 生日
*/
public UserInfo(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
/**
* 序列化
*
* @return 用户信息
*/
@Override
public String toString() {
return "用户名:" + name + "\n生日:" + birthday.toString() + "\n今日折扣率:" + discountRate*100 + "%\n";
}
}
4.3 VipUserInfo
VipUserInfo是VIP用户信息类,派生自UserInfo类。除了用户名、用户生日、用户享受的折扣率外,还声明了用户VIP等级等信息,同时重写了用户序列化方法。对应于工厂方法模式的参与者,VipUserInfo是我们的具体产品类。下面的代码给出了VipUserInfo的声明。
package demo.designpattern.factorymethod;
import java.util.Date;
/**
* vip用户信息
* Created by LiMingzi on 2017/5/9.
*/
public class VipUserInfo extends UserInfo {
/**
* vip等级
*/
public int level;
/**
* 构造方法
*
* @param name 用户名
* @param birthday 生日
* @param level vip等级
*/
public VipUserInfo(String name, Date birthday, int level) {
super(name, birthday);
this.level = level;
}
/**
* 构造方法
* @param userInfo 用户信息
* @param level vip等级
*/
public VipUserInfo(UserInfo userInfo,int level){
super(userInfo.name, userInfo.birthday);
this.level = level;
}
/**
* 序列化
*
* @return 用户信息
*/
@Override
public String toString() {
return super.toString()+"VIP等级:" + level + "\n";
}
}
4.4 UserMgmt
UserMgmt类是用户管理类,主要声明了创建用户信息对象的方法createUserInfo和输出用户信息的方法outputUserInfo。对应于工厂方法模式的参与者,UserMgmt是我们的创建者父类,createUserInfo是工厂方法。下面的代码给出了UserMgmt的声明。
package demo.designpattern.factorymethod;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
/**
* 用户管理类
* Created by LiMingzi on 2017/5/9.
*/
public class UserMgmt {
/**
* 输出用户信息
* @param userId 用户id
*/
public void outputUserInfo(String userId){
// 用户信息
UserInfo userInfo = createUserInfo(userId);
System.out.println("用户信息:");
System.out.println(userInfo.toString());
System.out.println("------------------------------------------------------------------");
}
/**
* 创建用户信息对象
* @param userId 用户id
* @return 用户信息对象
*/
protected UserInfo createUserInfo(String userId){
// 用户基本信息
Object[] userBaseInfo = getUserBaseInfo(userId);
// 用户信息
UserInfo userInfo = new UserInfo((String) userBaseInfo[0],(Date) userBaseInfo[1]);
// 今日日期
Calendar today = Calendar.getInstance();
today.setTime(new Date());
// 会员日全场8折
if(28 == today.get(Calendar.DAY_OF_MONTH)){
userInfo.discountRate = 0.2f;
}
else
{
userInfo.discountRate = 0.0f;
}
return userInfo;
}
/**
* 获取用户信息(demo)
* @param userId 用户id
* @return 用户信息,item[0]为用户名,item[1]为用户生日
*/
private Object[] getUserBaseInfo(String userId){
// 用户信息
Object[] userBaseInfo = new Object[2];
if("001".equals(userId)){
userBaseInfo[0] = "张三";
userBaseInfo[1] = new Date(new GregorianCalendar(1985,10,12).getTimeInMillis());
}
else if("002".equals(userId)){
userBaseInfo[0] = "李四";
userBaseInfo[1] = new Date(new GregorianCalendar(1992,5,14).getTimeInMillis());
}
return userBaseInfo;
}
}
上述代码中展示了工厂方法经常被使用的一种模式——隐含调用。即工厂方法createUserInfo并没有被Client直接调用生成对象,而是在公共方法outputUserInfo的实现中间接调用createUserInfo生成具体产品。
4.5 VipUserMgmt
VipUserMgmt是VIP用户管理类,派生自UserMgmt。对应于工厂方法模式的参与者,VipUserMgmt是我们的具体创建者。重写的工厂方法createUserInfo返回具体产品VipUserInfo的对象。下面的代码给出了VipUserMgmt的声明。
package demo.designpattern.factorymethod;
/**
* vip用户管理类
* Created by LiMingzi on 2017/5/9.
*/
public class VipUserMgmt extends UserMgmt{
/**
* 创建用户信息对象
* @param userId 用户id
* @return 用户信息对象
*/
protected VipUserInfo createUserInfo(String userId){
// 用户信息
UserInfo userInfo = super.createUserInfo(userId);
// 用户vip等级
int level = getUserLevel(userId);
// vip用户信息
VipUserInfo vipUserInfo = new VipUserInfo(userInfo,level);
// vip会员全场九折
if(vipUserInfo.discountRate<0.1f){
vipUserInfo.discountRate = 0.1f;
}
return vipUserInfo;
}
/**
* 获取用户等级(此处为demo)
* @param id 用户id
* @return 用户vip等级
*/
private int getUserLevel(String id){
if("002".equals(id)){
return 3;
}
else {
return 1;
}
}
}
上面代码第13行重写的工厂方法首先调用父类的工厂方法创建父产品对象,再用父产品对象与附加信息进一步创建具体产品对象。这是工厂方法的典型用法,同时应该注意具体产品应包含使用产品父类实例作为参数的构造方法以实现更好的封装性。
4.6 UserInfoViewer
UserInfoViewer实现了用户信息的查看功能,对应于工厂方法模式的参与者,UserInfoViewer是Client。下面的代码给出了UserInfoViewer的声明。
package demo.designpattern.factorymethod;
/**
* 用户信息查看器
* Created by LiMingzi on 2017/5/9.
*/
public class UserInfoViewer {
/**
* 是否为vip用户(demo)
* @param userId 用户id
* @return 是否为vip用户
*/
private boolean isVip(String userId){
if("002".equals(userId)){
return true;
}
else{
return false;
}
}
/**
* 查看用户信息
* @param userId 用户id
*/
public void viewUserInfo(String userId){
// 用户管理器
UserMgmt userMgmt;
if(isVip(userId)){
userMgmt = new VipUserMgmt();
}
else {
userMgmt = new UserMgmt();
}
userMgmt.outputUserInfo(userId);
}
}
从上述代码中可以看到,UserInfoViewer是知道应该用哪个类对象来实例化UserMgmt的。
4.7 测试代码
为了测试本文中的代码,我们可以编写如下测试代码。
/**
* 工厂方法测试
*/
public static void factoryMethodTest(){
// 用户信息查看器
demo.designpattern.factorymethod.UserInfoViewer userInfoViewer = new demo.designpattern.factorymethod.UserInfoViewer();
userInfoViewer.viewUserInfo("001");
userInfoViewer.viewUserInfo("002");
}
编译运行后,得到如下测试结果:
用户信息:
用户名:张三
生日:Tue Nov 12 00:00:00 CST 1985
今日折扣率:0.0%
用户信息:
用户名:李四
生日:Sun Jun 14 00:00:00 CST 1992
今日折扣率:10.0%
VIP等级:3