前言
设计模式是对代码开发经验的总结,代表着最佳编程实践,对于立志成为优秀工程师的我们,这一技能必不可。本系列会针对其特点、实现方式及应用场景做全面解析。
简单工厂
简单工厂其实不是一个设计模式,而是一种编程习惯,但是引用非常普遍
我们通过一个范例,来认识它,范例代码来源于@《Head First设计模式》。
现在有一个披萨店,Pizza品种繁多,供不应求,我们称之为萌芽期。
常规程式设计,我们通过传递不同参数,实例化Pizza:
Pizza orderPizza(String type) {
Pizza pizza;
// 不同的披萨
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equal("greek")) {
pizza = new GreekPizza();
} else if (type.equal("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equal("clam")) {
pizza = new ClamPizza();
} else if (type.equal("veggie")) {
pizza = new VeggiePizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
但是后面PizzaStore为迎合大众口味,推出不同的PizzaType,我们在原有程式做修改,就违背了开闭原则。
简单工厂可以帮我们解决困扰,我们将变化的部分抽离出来,打包到简单工厂类中
public class SimplePizzaFactory {
public Pizza createPizza(Striing type) {
Pizza piizza =null;
if (type.equals("cheese")) {
pizza = new CheesePizza();
} else if (type.equal("greek")) {
pizza = new GreekPizza();
} else if (type.equal("pepperoni")) {
pizza = new PepperoniPizza();
} else if (type.equal("clam")) {
pizza = new ClamPizza();
} else if (type.equal("veggie")) {
pizza = new VeggiePizza();
}
}
}
PizzaStore接入工厂代码模块:
public class PizzaStore {
SimplePizzaFactory factory;
public PizzaStore(SimplePizzaFactory factory) {
this.factory = factory;
}
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = factory.createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
优势:
- 解耦后的工厂类,可以被其他服务复用,eg:Pizza菜单、Pizza外卖服务等
- 符合开闭原则,pizza种类的变更,单独维护工厂类即可
工厂模式
PizzaStore由于越做越好,已经在芝加哥和纽约开设了分店,两地口味差别较大,原来的架构只适应多种Pizza制作,无法适应门店新增的业务诉求。我们称之为发展期。
如果将业务抽离到Pizza-产品组、Pizza门店-创建组,结构就会清晰很多,也具备更高的扩展性。
public abstract class PizzaStore {
publc Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
abstract Pizza createPizza(string type);
}
抽象的PizzaStore父类交出控制权,由子类自己确定实例化哪个具体的pizza
public class NYPizzaStore extends PizzaStore {
Pizza createPizza(String item) {
if (item.equals("cheese")) {
pizza = new NYStyleCheesePizza();
} else if (item.equal("greek")) {
pizza = new NYStyleGreekPizza();
} else if (item.equal("pepperoni")) {
pizza = new NYStylePepperoniPizza();
}
return pizza;
}
}
针对我们的Pizza产品组,我们用同样思路做父类抽象和子类继承:
public abstract calss Pizza {
String name;
String dough;
String sauce;
ArrayList toppings = new ArrayList();
void prepare() {
System.out.println("Preparing "+name);
System.out.println("Tossiing dough..");
System.out.println("Adding sauce..");
System.out.println("Adding toppings: ");
for (int i=0; i<toppings.size();i++) {
System.out.println(" " + toppings.get(i));
}
}
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public string getName() {
return name;
}
}
子类继承,并根据业务需求,定制化美味的Pizza:
public class NYStyleCheesePizza extends Pizza {
public NYStyleCheesePizza() {
name = "NY Style Sauce and Cheese Pizza";
dough = "Thin Crust Dough";
sauce = "Marinara Sauce";
toppings.add("Grated Reggiano Cheese");
}
// 覆盖原cut方法
void cut() {
System.out.println("Cutting the pizza into square slices");
}
}
抽象工厂模式
Pizza发展极快,已经风靡全美,门店越来越多,此时,品控显的尤为重要,为了杜绝纽约店、加利福利亚店、佛罗里达店等原材料品质不一,导致Pizza质量问题,我们可以提供原材料集中供应,解决此问题。这一时期,我们称之为成熟期。
我们将业务拆解为抽象原料工厂、纽约州原料工厂、加利福利亚州原料工厂、纽约州商店、以及各类抽象Pizza材料、Pizza材料
抽象原料工厂:
public interface PizzaIngredientFactory {
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClam();
}
纽约州原料工厂:
public class NYPizzaIngredientFactory implements PizzaIngredientFactory {
public Dough createDough() {
return new ThinCrustDough();
}
public Sauce createSauce() {
return new MarinaraSauce();
}
public Cheese createCheese() {
return new ReggianoCheese();
}
public Veggies[] createVeggies() {
Veggies veggies[] = {new Garlic(), new Onion(), new MushRoom(), new RedPepper()}
return Veggies;
}
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
public Clams createClam() {
return new FreshClams();
}
}
抽象Pizza类:
public abstract calss Pizza {
String name;
String dough;
String sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperonil
Clams clam;
abstract void prepare();
void bake() {
System.out.println("Bake for 25 minutes at 350");
}
void cut() {
System.out.println("Cutting the pizza into diagonal slices");
}
void box() {
System.out.println("Place pizza in official PizzaStore box");
}
public string setName() {
this.name = name;
}
public string getName() {
return name;
}
public String toString() {
// 打印Pizza代码
}
}
Pizza实例:
public class CheesePizza extends Pizza {
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory) {
this.ingredientFactory = ingredientFactory;
}
void prepare() {
System.out.println("Preparing" + name);
dough = ingredientFactory.createDough();
sauce = ingredientFactory.createSauce();
cheese = ingredientFactory.createCheese();
}
}
PizzaStore实例:
public class NYPizzaStore extends PizzaStore {
protected Pizza createPizza(String item) {
Pizza pizza = null;
Pizza IngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (item.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("NEW York Style Cheese Pizza");
} else if (item.equals("veggie")) {
pizza = new VeggiePizza(ingredientFactory);
pizza.setName("NEW York Style veggie Pizza");
}
}
}
简单工厂实践
业务场景:每月都有新增活动,比如二月租屋买3送1,三月售屋优惠券,以及五月售屋五折,该如何设计实现呢?
活动包含四个必要因素:活动资格、活动方案、活动金流、以及活动有效期,我们提取公共方法,并提供抽象活动类,让每个活动子类继承实现。
abstract class Act{
public $data = array(); //活动明细
abstract function checkRoleIsRight($data); //活动资格
abstract function actProgram($data); //活动方案
abstract function actMoney($data); //活动金流
/**
* 檢測使用是否在有效期
*/
function checkActIsExpires($actStartTime, $actEndTime) {
$startTime = strtotime($actStartTime);
$endTime = strtotime($actEndTime);
return (NOW_TIME >= $startTime && NOW_TIME <= $endTime) ? 1 : 0;
}
}
子活动继承并实现业务逻辑
class salNewHalfAct extends Act{
public $actStartTime = '2020-01-01 00:00:00';
public $actEndTime = '2022-01-01 23:59:59';
function checkRoleIsRight($data){
//判断是否在活动时间内
$isExpires = $this->checkActIsExpires($this->actStartTime, $this->actEndTime);
return !$isExpires ? false : true;
}
function actProgram($data){}
function actMoney($data){}
}
特点
- 公共方法抽离,并实现通用方法,让代码更简洁
- 业务需求只需要在特定actProgram、actMoney方法内实现,具备良好维护及扩展性
总结
我们根据Pizza店不同阶段运营情况,引入不同工厂以适应其业务:萌芽期、发展期、成熟期的需求。
接着,我们通过业务活动案例,实践了简单工厂。
由于当前互联网业务迭代极快,过多冗余的使用设计模式反而会适得其反,个人建议大家可以从成熟的业务模块入手,相信会事半功倍!
参考资料:
《Head First设计模式》