设计模式04-创建型模式1(简单工厂/工厂模式/抽象工厂/Java)
3.1 简单工厂模式
3.1.1 创建型模式
创建型设计模式将对象的创建过程和对象的使用过程分离,用户使用对象时无需关注对象的创建细节,外界对于这些对象只需要知道它们共同的接口,而不用清楚其实现细节,使得整个系统的设计更加符合单一职责原则。软件的结构也更为清晰。
3.1.2 简单工厂模式的定义
专门定义一个类来负责创建其他类的实例,可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
3.1.3 简单工厂模式的分析与实现
-
工厂角色:即工厂类,简单工厂模式的核心,负责实现创建所有实例的内部逻辑;工厂类可被外部直接调用,创建所需的产品对象;工厂类中提供了静态的工厂方法(它返回一个抽象产品类Product,所有具体产品都是抽象产品的子类。
-
工厂类中只有简单的逻辑判断代码。不关心具体的业务处理过程,满足“单一职责原则”。
-
调用工厂类的工厂方法时,由于工厂方法时静态方法,使用很方便,可通过类名直接调用,只需要传入一个简单的参数即可。
3.1.4 简单工厂模式的案例
某电视机厂专为各知名电视机品牌代工生产各类电视机,当需要海尔牌电视机时只需要在调用该工厂的工厂方法时传入参数“Haier”,需要海信电视机时只需要传入参数“Hisense”,工厂可以根据传入的不同参数返回不同品牌的电视机。现使用简单工厂模式来模拟该电视机工厂的生产过程。
- 抽象产品类和具体产品类
public interface TV {
public void play();
}
public class HaierTV implements TV{
@Override
public void play() {
System.out.println("Haier电视正在播放");
}
}
public class HisenseTV implements TV{
@Override
public void play() {
System.out.println("hisense电视正在播放");
}
}
- 工厂类
public class TVFactory {
public static TV getTVMethod(String flag) throws Exception {
if ("Haier".equalsIgnoreCase(flag)) {
return new HaierTV();
} else if ("Hisense".equalsIgnoreCase(flag)) {
return new HisenseTV();
} else {
throw new Exception("抱歉没有此类电视");
}
}
- Main类
public class Main {
public static void main(String[] args) {
String tvName = XMLUtilTV.getTVName();
try {
TV tvMethod = TVFactory.getTVMethod(tvName);
tvMethod.play();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class XMLUtilTV {
public static String getTVName() {
try {
//创建文本对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document document = db.parse(new File("src/main/resources/TVBrand.xml"));
//通过DOM获取文本元素
NodeList brandName = document.getElementsByTagName("brandName");
Node content = brandName.item(0).getFirstChild();
return content.getNodeValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
<?xml version="1.0" encoding="ISO-8859-1"?>
<config>
<brandName>Hisense</brandName>
</config>
3.1.5 简单工厂模式的优缺点
优点 | 缺点 |
---|---|
1.实现了对象的创建和使用分离 | 1.工厂类职责太重,不宜维护 |
2.无需知道类名,只需要知道参数 | 2.系统扩展难度大,且工厂类为静态类不能进行扩展 |
3.1.6 简单工厂模式适用场景
- 工厂类负责创建的对象比较少,由于创建对象比较少。
- 客户端只知道传入工厂类的参数,对于如何创建对象不关心。
3.2 工厂模式
3.2.1 工厂模式的定义
动机:为了解决简单工厂模式不易扩展以及工厂类职责太重的为问题。
在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口, 而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定 究竟应该实例化哪一个具体产品类。
3.2.2 工厂模式的分析与实现
-
核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给其子类去完成
-
可以允许系统在不修改工厂角色的情况下引进新产品,增加具体产品–>增加具体工厂,符合“开闭原则”。
3.2.3 工厂模式的案例
将原有的电视机工厂进行分割,为每种品牌的电视机提 供一个子工厂,海尔工厂专门负责生产海尔电视机,海 信工厂专门负责生产海信电视机,如果需要生产TCL电视 机或创维电视机,只需要对应增加一个新的TCL工厂或创 维工厂即可,原有的工厂无须做任何修改,使得整个系 统具有更加的灵活性和可扩展性。
- 产品类
public interface TV {
public void play();
}
public class HaierTV implements TV{
@Override
public void play() {
System.out.println("Haier电视正在播放");
}
}
public class HisenseTV implements TV{
@Override
public void play() {
System.out.println("Hisense电视正在播放");
}
}
- 工厂类
public interface TVFactory {
public TV produceTV();
}
public class HaierTVFactory implements TVFactory{
@Override
public TV produceTV() {
System.out.println("海尔电视已经被制造");
return new HaierTV();
}
}
public class HisenseTVFactory implements TVFactory{
@Override
public TV produceTV() {
System.out.println("海信电视已被制造");
return new HisenseTV();
}
}
- 调用(为了更好的满足开闭原则,这里使用Java的反射机制来代替new关键字)
public class XMLUtilTVFactory {
public static Object getTVFactoryMethod() throws Exception{
//获取文本对象
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File("src/main/resources/TVFactoryMethod.xml"));
//获取文本内容
NodeList nodeList = doc.getElementsByTagName("factoryName");
Node firstChild = nodeList.item(0).getFirstChild();
String nodeValue = firstChild.getNodeValue();
//通过反射获取对象
Class className = Class.forName(nodeValue);
Object obj = className.newInstance();
return obj;
}
}
public class Main {
public static void main(String[] args) {
try {
HisenseTVFactory tvFactoryMethod = (HisenseTVFactory) XMLUtilTVFactory.getTVFactoryMethod();
TV tv = tvFactoryMethod.produceTV();
tv.play();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
xml:
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<factoryName>com.tyut.factory_method.example2.HisenseTVFactory</factoryName>
</config>
3.2.4 工厂模式的优缺点
优点 | 缺点 |
---|---|
1.实现了对象的创建和使用分离,将对象的创建细节全部封装在工厂中 | 1.系统中的类的个数成对出现,加重系统的负担 |
2.在系统中添加新产品,完全符合开闭原则 |
3.2.5 工厂模式的适用场景
- 客户端不需要知道具体 产品类的类名,只需要知道所对应的工厂即可,具体产品对 象由具体工厂类创建
- 抽象工厂类通过其子类来指定创建哪个对象
3.3 抽象工厂模式
3.3.1 抽象工厂模式的定义
动机:需要一个工厂,生产多个对象
产品等级结构:产品的继承结构(一个产品的不同表现形式:名词)
产品族:指由同一个工厂生产的,位于不同产品等级结构中的一组产品(形容词)
定义:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。
3.3.2 抽象工厂模式的分析与实现
- 抽象工厂中定义了多个方法,每一个方法代表一个产品等级
- 具体工厂即表示每一个产品族的工厂
3.3.3 抽象工厂的案例
一个电器工厂可以产生多种类型的电器,如海尔工厂可以生产海尔电视机、海尔空调等,TCL工厂可以生产TCL电视机、TCL空调等,相同品牌的电器构成一个产品族,而相同类型的电器构成了一个产品等级结构,现使用抽象工厂模式模拟该场景。
- 产品类
public interface TV {
public void play();
}
public class HaierTV implements TV{
@Override
public void play() {
System.out.println("海尔电视正在播放");
}
}
public class HisenseTV implements TV{
@Override
public void play() {
System.out.println("海信电视正在播放");
}
}
public interface Conditioner {
public void work();
}
public class HaierConditioner implements Conditioner{
@Override
public void work() {
System.out.println("海尔空调正在工作");
}
}
public class HisenseConditioner implements Conditioner{
@Override
public void work() {
System.out.println("海信空调正在工作");
}
}
- 工厂类
public interface BrandFactory {
public TV creatTV();
public Conditioner createConditioner();
}
public class HisenseFactory implements BrandFactory{
@Override
public TV creatTV() {
System.out.println("海信电视已被制作");
return new HisenseTV();
}
@Override
public Conditioner createConditioner() {
System.out.println("海信空调已被制作");
return new HisenseConditioner();
}
}
public class HaierFactory implements BrandFactory{
@Override
public TV creatTV() {
System.out.println("海尔电视已被制作");
return new HaierTV();
}
@Override
public Conditioner createConditioner() {
System.out.println("海尔空调已被制作");
return new HaierConditioner();
}
}
- Main类
public class Main {
public static void main(String[] args) {
try {
BrandFactory factory = (HisenseFactory) XMLUtilBrandFactory.getFactory();
TV tv = factory.creatTV();
tv.play();
Conditioner conditioner = factory.createConditioner();
conditioner.work();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class XMLUtilBrandFactory {
public static Object getFactory() throws Exception{
//获取XML文本
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File("src/main/resources/BrandFactory.xml"));
//获取工厂名字
NodeList nodeList = doc.getElementsByTagName("brandFactory");
Node firstChild = nodeList.item(0).getFirstChild();
String className = firstChild.getNodeValue();
//通过反射获取工厂对象
Class<?> aClass = Class.forName(className);
Object obj = aClass.newInstance();
return obj;
}
}
3.3.4 抽象工厂的优缺点
优点 | 缺点 |
---|---|
1.实现了对象的创建和使用分离。 | 1.增加新的产品等级结构麻烦,违背了开闭原则 |
2.当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象 |
3.3.5从抽象工厂模式的适用场景
- 系统中有多于一个的产品族,但每次只使用其中某一产品族
- 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节