作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
学习必须往深处挖,挖的越深,基础越扎实!
阶段1、深入多线程
阶段2、深入多线程设计模式
阶段3、深入juc源码解析
阶段4、深入jdk其余源码解析
阶段5、深入jvm源码解析
码哥源码部分
码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】
码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】
码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】
码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】
码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】
码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】
码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】
终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!
打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】
所谓工厂,就是将零件组装成产品的地方。
建一个对象常常需要复杂的过程,所以不适合包含在一个复合对象
中。创建对象可能会导致大量的重复代码,可能会需要复合对象访问不到的信息,也可能提供不了足够级别的抽象,还可能并不是复合对象概念的一部分。
在面向对象程序设计中,工厂通常是一个用来创建其他对象
的对象。工厂是构造方法的抽象,用来实现不同的分配方案。
一般情况下,工厂模式分为三种更加细分的类型: 简单工厂、工厂方法和抽象工厂 。不过,在GoF的《设计模式》一书中,它将简单工厂
看作是工厂方法模式
的一种特例,所以工厂模式
只被分成了工厂方法
和抽象工厂
两类。实际上,前一种分类方法更加常见。
实际上,简单工厂
、工厂方法
原理比较简单,实际的项目也比较常用。而抽象工厂的原理稍微复杂,在实际项目相对也不常用。对于抽象工厂方法
了解即可。
几个不同的设计模式都应用了工厂的概念,并可以使用在很多语言中。例如,在《设计模式》一书中,像工厂方法模式、抽象工厂模式、生成器模式,甚至是单例模式都应用了工厂的概念。
/**
* Pizza 简单工厂类
*/
public class SimplePizzaFactory {
/**
* 根据不同pizza类型创建对应Pizza
* @param type
* @return
*/
public Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new CheesePizza();
} else if (type.equals("veggie")) {
return new Veggie("veggiePizza");
} else {
return null;
}
}
}
public class Veggie extends Pizza {
public Veggie(String name) {
setName(name);
}
}
public class T {
public static void main(String[] args) {
// 1. 创建Pizza简单工厂
SimplePizzaFactory simplePizzaFactory = new SimplePizzaFactory();
// 2.根据type创建Pizza
Pizza cheese = simplePizzaFactory.createPizza("veggie");
// 3.得到Pizza
cheese.toString();
}
}
通过简单的if/else
判断,创建对应的pizza
。如果要添加新的pizza
,势必要改动源码,那这违反开闭原则呢? 实际上,对于不需要频繁添加新的pizza
,也是完全可以接受的。
工厂方法模式
定义了一个创建对象的接口,但由子类决定
要实例化的类是哪一个。工厂方法让类把实例化推迟到子类
。工厂方法模式帮助我们将产品的
实现
从使用
中解耦。如果增加产品或者改变产品的实现。Creator类
并不会受影响。
层级平等
: 因为它们都有抽象类,而抽象类都有许多具体的子类,每个子类都有自己的实现。 /**
* PizzaStore 作为超类,可以被其他
* 各个加盟店之间的区别在于风味不同,而其它流程我们认为是相同的
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza;
pizza = createPizza(type);
System.out.println(pizza.getName());
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* ① 工厂方法是抽象的。所以依赖子类来处理对象的创建
* ② 必须返回一个产品
* ③ 工厂方法有可能需要参数,也有可能不需要参数来指定所需要的产品
* @param type
* @return
*/
protected abstract Pizza createPizza(String type);
}
/**
* 芝加哥 Pizza风味店
*/
public class ChicagoStylePizzaStore extends PizzaStore{
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new ChicagoStyleCheesePizza();
} else {
return null;
}
}
}
public class M {
public static void main(String[] args) {
System.out.println("The first customer order in Chicago pizza store");
PizzaStore chicagoStylePizzaStore = new ChicagoStylePizzaStore();
Pizza pizza = chicagoStylePizzaStore.orderPizza("cheese");
System.out.println("The second customer order in NY pizza store");
NYStylePizzaStore nyStylePizzaStore = new NYStylePizzaStore();
nyStylePizzaStore.orderPizza("cheese");
}
}
开闭-原则
。即对扩展开发、对修改关闭
。因为每增加一个产品类,需要在工厂代码里面增加if-else
判断。之所以将某个代码块剥离独立为函数或类,原因是这个代码块的逻辑过于复杂,剥离之后能让代码更加清晰,更加可读、可维护。但是,如果代码块本身并不复杂,就几行代码而已,我们完全没必要将它拆分成单独的函数或者类。
基于这个设计思想,当对象的创建逻辑比较复杂,不只是简单的new
一下,而是要组合其他类对象,做各种初始化操作的时候,推荐使用工厂方法模式
,将复杂的创建逻辑拆分到多个工厂类中,让每个工厂类都不至于过于复杂。而使用简单工厂模式,将所有的创建逻辑都放到一个工厂类中,会导致这个工厂类变得复杂。
除此之外,在某些场景下,如果对象不可复用,那工厂类每次都要返回不同对象。如果我们使用简单工厂模式实现,就只能选择第一种包含if
分支逻辑的实现方式。如果我们还想避免烦人的if-else
分支逻辑,推荐使用工厂方法模式
。
抽象工厂模式 (英语: Abstract factory pattern )是一种软件开发设计模式。抽象工厂模式提供了一种方式,可以将一组具有同一主题的单独的工厂封装起来。在正常使用中,客户端程序需要创建抽象工厂的具体实现,然后使用抽象工厂作为接口来创建这一主题的具体对象。客户端程序不需要知道(或关心)它从这些内部的工厂方法中获得对象的具体类型,因为客户端程序仅使用这些对象的通用接口。抽象工厂模式将一组对象的实现细节与他们的一般使用分离开来。
工厂
是创建产品(对象)的地方,其目的是将产品的创建与产品的使用分离。抽象工厂模式的目的,是将若干抽象产品的接口与不同主题产品的具体实现分离开。这样就能在增加新的具体工厂的时候,不用修改引用抽象工厂的客户端代码。使用抽象工厂模式,能够在具体工厂变化的时候,不用修改使用工厂的客户端代码,甚至是在运行时。抽象工厂模式的实质是“提供接口,创建一系列相关或独立的对象,而不指定这些对象的具体类。
(引用维基百科)
抽象工厂为产品家族提供接口。通过抽象工厂所提供的接口,可以创建产品的家族,复用这个接口书写代码,我们的代码将从实际工厂解耦,以便在不同上下文中实现各式各样的工厂,制造出各种不同的产品。
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。
抽象工厂允许客户使用抽象的接口来创建一组相关的产品,而不需要知道(或关心)实际产出的具体产品是什么。这样一来,客户就从具体的产品中被解耦。
抽象工厂的方法经常以工厂方法的方式实现。
(引用Head First 设计模式)
抽象工厂的工作就是将
抽象零件
组装为抽象产品
。抽象
是指不考虑具体怎么实现,而是仅仅只确定了方法的名字和签名
。
抽象工厂
确定工厂的业务范围。
具体工厂
每个具体工厂对应一个产品族
。具体工厂决定生产哪个具体的产品对象。
抽象产品
同一产品等级结构的抽象类。
具体产品
可供生产的具体产品。
/**
* Pizza 原料工厂接口
* 定义如何产生一个相关产品的家族。这个家族包含了所有制作Pizza的原料
*/
public interface PizzaIngredientFactory {
Dough createDough();
Sauce createSauce();
Cheese createCheese();
}
/**
* 该工厂根据自身类型生产原料
*/
public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
@Override
public Dough createDough() {
return new ThinCrustDough();
}
@Override
public Sauce createSauce() {
return new MarinaraSauce();
}
@Override
public Cheese createCheese() {
return new ReggianoCheese();
}
}
/**
* Cheese原料定义
*/
public abstract class Cheese {
}
/**
* 酱料
*/
public abstract class Sauce {
}
public class MarinaraSauce extends Sauce {
}
public class ReggianoCheese extends Cheese {
}
/**
* Pizza店 抽象类
*/
public abstract class PizzaStore {
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type);
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
/**
* ① 工厂方法是抽象的。所以依赖子类来处理对象的创建
* ② 必须返回一个产品
* ③ 工厂方法有可能需要参数,也有可能不需要参数来指定所需要的产品
* @param type
* @return
*/
protected abstract Pizza createPizza(String type);
}
/**
* Pizza店具体实例
* 是抽象工厂的客户
*/
public class NYPizzaStore extends PizzaStore{
/**
* 根据类型创建Pizza
* @param type
* @return
*/
@Override
protected Pizza createPizza(String type) {
Pizza pizza = null;
// 通过
PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();
if (type.equals("cheese")) {
pizza = new CheesePizza(ingredientFactory);
pizza.setName("New York Style Cheese Pizza");
return pizza;
}
return null;
}
}
public class T {
public static void main(String[] args) {
// 创建纽约Pizza店
PizzaStore pizzaStore = new NYPizzaStore();
// 接受订单
pizzaStore.orderPizza("cheese");
}
}
要依赖抽象,不要依赖具体类
。
指导方针
变量不可以持有具体类的引用
如果使用new
关键字,就会持有具体类的引用。你可以改用工厂来避开这样的做法。
不要让类派生自具体类
如果派生自具体类,你就会依赖这个具体类。请派生自一个抽象(接口或抽象类)。
不要覆盖基类中已实现的方法。
如果覆盖基类已实现的方法,那么你的基类就不是一个真正适合被继承的抽象。基类中已实现的方法,应该由所有的子类共享。
当然,完全遵守这些指导方针似乎不太可能,应该尽量达到这个原则,而不是随时都要遵循这个原则。
继承
实现对象的创建。而抽象方法使用组合
。工厂方法只负责将客户从具体类型中解耦。而抽象工厂提供一个用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法。