制造对象,不仅只有使用new操作符。实例化不应该总是公开进行,因为经常会导致耦合问题。
01例子
假设你有一个披萨店,身为披萨店的主人,代码可能是这样:
Pizza orderPizza(){
//为了让系统有弹性,我们很希望这是一个抽象类或接口。但如果这样,这些类或接口就无法直接实例化。
Pizza pizza =new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
需要更多披萨类型时:
Pizza orderPizza(String type){
Pizza pizza ;
if(type.equals("cheese")){
pizza=new CheesePizza();
}else if(type.equals("greek")){
pizza=new GreekPizza();
}else if(type.equals("pepperoni")){
pizza=new PepperoniPizza();
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
缺点:如果要增加或者删除某个type,需要对这份代码进行修改。
关于哪个具体类被实例化才是搞乱orderPizza()方法的祸首:
它无法让orderPizza()对修改关闭;但是,现在我们已经知道哪些会改变,哪些不会改变,该是封装的时候了。
02简单工厂
建立简单工厂
public class SimplePizzaFactory{
public Pizza createPizza(String type){
Pizza pizza=null;
if(type.equals("cheese")){
pizza=new CheesePizza();
}else if(type.equals("pepperoni")){
pizza=new PepperoniPizza();
}else if(type.equals("clam")){
pizza=new ClamPizza();
}else if(type.equals("veggie")){
pizza=new VeggiePizza();
}
return pizza;
}
}
重写PizzaStore类
依赖工厂来创建pizza
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;
}
}
类图
简单工厂不是设计模式,是一种编程习惯!
03工厂方法
加盟披萨店
如果利用SimplePizzaFactory,写出三种不同的工厂,分别是
NYPizzaFactory,ChicagoPizzaFactory,CaliforniaPizzaFactory,各地加盟店都有适合的工厂可以使用,这是一种做法。
NYPizzaFactory nyFactory=new NYPizzaFactory();//创建纽约风味pizza的工厂
PizzaStore nyStore=new PizzaStore(nyFactory);//创建pizzastore
nyFactory.orderPizza("Veggie");//制作pizza,得到纽约风味的;
缺点:不能质量控制;目的:只想创建pizza不同,其他操作相同;
框架
02简单工厂中,在SimplePizzaFactory前,把制作pizza绑到了store,失去了弹性。【需要】:创建框架,把加盟店和pizza创建绑定在一起,但允许保持弹性
有个做法可让披萨制作轰动局限于PizzaStore类,而同时又能让这些加盟店依然可以自由地制作该区域的风味。
把createPizza()方法放回到PizzaStore中,不过要把它设置成“抽象方法”,然后为每个区域风味创建一个PizzaStore的子类。
public abstract class PizzaStore(){
public Pizza orderPizza(String type){
Pizza pizza;
pizza=createPizza(type);//从工厂对象移回pizzastore;【不是factory.createPizza()】
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
public abstract Pizza createPizza(String type);//把工厂对象移到这个方法中
//作为抽象方法,为每个区域风味创建一个pizzastore子类
}
子类:
orderPizza()方法在抽象的pizzastore中定义,所以,该方法不知道哪个子类实际运行代码并制作pizza;【即不知道哪个实际的具体类【解耦了】】
实现
加盟店只需要继承pizzastore,并提供自己的pizza风味的creatPizza()方法。
纽约风味店:
class NYPizzaStore extends PizzaStore{
Pizza createPizza(String type){
if(type.equals("cheese")){
pizza=new NYStyleCheesePizza();//创建具体类
}else if(type.equals("pepperoni")){
pizza=new NYStylePepperoniPizza();
}else if(type.equals("clam")){
pizza=new NYStyleClamPizza();
}else if(type.equals("veggie")){
pizza=new NYStyleVeggiePizza();
}
}
}
工厂方法(以上框架)
对pizzastore类做一些转换,从由一个对象处理具体类的实例化,到由一个子类集合来承担责任。
public abstract class PizzaStore(){
public Pizza orderPizza(String type){
Pizza pizza;
pizza=createPizza(type);//从工厂对象移回pizzastore;【不是factory.createPizza()】
//pizzastore的子类在creatpizza()中处理对象的实例化
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
public abstract Pizza createPizza(String type);//把工厂对象移到这个方法中
//作为抽象方法,为每个区域风味创建一个pizzastore子类
//实例化pizza的责任被转到了方法中(该方法扮演工厂的角色)
}
Pizza类
public abstract class Pizza{
String name;
String dough;
String sauce;
ArrayList toppings =new ArrayList();
public void prepare(){
System.out.println("Preparing"+name);
System.out.println("Tossing 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));
}
}
public void bake(){
System.out.println("Bake for 25 minutes at 350");
}
public void cut(){
System.out.println("Cutting the pizza into diagonal slices");
}
public 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");
}
}
测试:
public class PizzaTestDrive{
public static void main(String[] args){
PizzaStore nyStore =new NYPizzaStore();
PizzaStore chicagoStore=new ChicagoPizzaStore();
Pizza pizza=nyStore.orderPizza("cheese);
System.out.println("Ethan ordered a"+pizza.getName()+"\n")
}
}
工厂方法模式
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
工厂方法模式能够封装具体类型的实例化,抽象的Creator提供了一个创建对象的方法的接口,在抽象的Creator中,任何其他实现的方法,都可能使用到这个工厂方法所制造出来的产品,但只有子类真正实现这个工厂方法并创建产品。
例子类图:
工厂方法是封装的关键
平行视角:平行的类层级
04依赖倒置原则
对象依赖
当你直接实例化一个对象时,就是在依赖它的具体类,返回前页看看这个依赖性很高的比萨店例子,它由披萨店类来创建所有的披萨对象,而不是委托给工厂。
依赖倒置原则:要依赖抽象,不要依赖具体类。
依赖倒置原则更强调抽象。说明高层组件不能依赖底层组件,而且,不管高层或底层组件,都应该依赖抽象。
PizzaStore是“高层组件”,而披萨实现是“底层组件”,很清楚地,PizzaStore依赖这些具体披萨类
应用原则
此时高层组件PizzaStore和底层组件(就是这些比萨)都依赖了Pizza抽象。
05抽象工厂
需求
建造一家成产原料的工厂,并将原料运送到各家加盟店【有些加盟店,使用低价原料来增加利润,你必须采取一些手段,以免回调你的披萨店平牌】
建造原料厂
public interface PizzaIngredientFactory{
public Dough createDough();
public Sauce createSauce();
public Cheese createCheese();
public Veggies[] createVeggies();
public Pepperoni createPepperoni();
public Clams createClams();
}
要做的事情是:
1.为每个区域建造一个工厂,需要创建一个继承自PizzaIngredientFactory的子类来实现每一个创建方法。
2.实现一组原料类供工厂使用,例如ReggianoCheese,RedPeppers,ThickCrustDough.这些类可以在何时的区域间共享。
3.然后你仍然需要将这一切组织起来,将新的原料工厂整合进旧的PizzaStore
创建原料工厂子类---纽约
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
return new ThinCrusDough();
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
return ReggianoCheese();
}
@Override
public Veggies[] createVeggies() {
Veggies veggies[] = {new Garlic(),new Onion(),new Mushroom(),new RedPepper()};
return new Veggies;
}
@Override
public Pepperoni createPepperoni() {
return new SlicedPepperoni();
}
@Override
public Clams createClam() {
return FreshClams();
}
}
重做Pizza类
public abstract class Pizza{
//每个pizza持有一组原料
String name;
Dough dough;
Sauce sauce;
Veggies veggies[];
Cheese cheese;
Pepperoni pepperoni;
Clams clams;
public abstract void prepare();//该方法收集来自工厂的原料
//除了prepare()方法,其他方法保持不变
public void cut(){
System.out.println("Cutting the pizza into diagonal slices");
}
public void box(){
System.out.println("Place pizza in official PizzaStore box");
}
public void setName(String name){
this.name=name;
}
public String getName(){
return name;
}
public void toString2(){
//这里打印披萨的代码
}
}
重做Pizza类的子类
class CheesePizza extends Pizza{
PizzaIngredientFactory ingredientFactory;
public CheesePizza(PizzaIngredientFactory ingredientFactory){
this.ingredientFactory=ingredientFactory;
}
@Override
public 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;
PizzaIngredientFactory ingredientFactory=new NYPizzaIngredientFactory();
if(item.equals("cheese")){
pizza=new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
}else if(item.equals("veggie")){
//……
}
return pizza;
}
}
抽象工厂
抽象工厂模式:提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
类图:
pizzastore类图
工厂方法和抽象工厂比较
工厂方法
用工厂方法将客户代码从需要实例化的具体类解耦,或者如果事先不知道会需要哪些具体类时,用工厂方法只需要子类化并实现工厂方法!
抽象工厂
任何时候需要创建产品家族,以及想确保客户创建相互关联的产品时,使用抽象工厂。
总结
OO原则:
封装变化
组合优于集承
针对接口编程、而不是针对实现
尽力达到交互对象之间的松耦合设计
类应该对扩展开放,对修改关闭
依赖于抽象,不要依赖于具体类
【我们有了一条新的原则,指导我们尽可能地让事情保持抽象。】
OO模式:
抽象工厂,提供一个接口,用于创建相关或依赖对象的家族,而不必指定它们的具体类
工厂方法,定义一个创建对象的接口,但让子类决定哪个类要实例化。工厂方法让一个类延迟实例化到子类。
要点
1.所有工厂都封装对象的创建。
2.简单工厂虽然不是真正体设计模式,但依然可以为一个简单的方法,将客户从具体类解耦
3.工厂方法靠继承:对象创建被委托给子类,子类实现工厂方法来创建对象
4.抽象工厂靠对象组合:对象创建在工厂接口暴露的方法中实现。
5.所有工厂模式都通过减少应用对具体类的依赖,促进了松耦合。
6.工厂方法的意图,是允许个类延迟实例化到其子类。
7.抽象工厂的意图,是创建相关对象家族,不必依赖于其具体类。
8.依赖倒置原则指导我们避免依赖具体类型,尽量依赖抽象。
9.工厂是强有力的技巧,让我们针对抽象编码,而不是针对具体类。