Bootstrap

Java设计模式之简单工厂、工厂方法、抽象工厂(超详细的讲解)

一、首先我们聊一聊设计模式的分类:
1.创建型模式(5种):工厂方法、抽象工厂、建造者模式、原型模式、单例模式;注意虽然简单工厂不是这23中的一员,但是它有助于我们的入门学习。创建型主要完成对象的创建工作。
2.结构型模式(7种):适配器、桥接、组合、装饰、外观、享元、代理;结构型主要是处理类或者对象的组合。
3.行为型模式(11种):这里就不一一列出来了。
二、接着我们聊一聊面向对象的设计的原则:
1.单一职责原则:也就是一个类的职责(实现的功能)要尽可能的单一,不能将太多的职责(实现的功能)放在这一个类里面;
2.开闭原则:指的是一个软件实体对扩展是开放的,但是对修改是关闭的(也就是不能对源代码进行修改),值得我们注意的是:XML与properties等格式的配置文件是纯文本的文件,是无法编译的,因此在软件开发的过程中把对这些文件的修改不认为是对系统源码的修改。如果一个系统中的扩展只涉及修改配置文件的话,而原有的Java代码没有做任何的修改,则认为该系统是一个符合开闭原则的系统。在Java中用抽象类或者接口来实现开闭原则。
3.里氏代换原则:在一个软件实体中,把所有对父类引用的地方用子类进行替换,而对系统无影响;

在这里插入图片描述

4.依赖倒转原则:要针对抽象层编程(针对接口编程,不针对实现编程),在设计方法的参数、返回值类型时、数据类型时、使用接口或者抽象类来进行声明。EJB、Spring等框架的背后就是基于此基本原则进行设计的。
5.接口隔离原则:在这里我们可以将一个一个的接口看做是一个一个的“角色”,那么此原则就是将一个大的接口划分为多个专门的接口(角色),使用此原则进行接口的划分时首先应该满足单一职责的原则(尽量将一个接口中的功能变得单一,而不是复杂)。
6.合成复用原则:在系统中多使用组合与聚合关系,尽量少使用甚至不用继承关系。
关于类与类之间的关系:

a.关联关系 (一个类对象与另一个类对象有关系)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

b.依赖关系
在这里插入图片描述
c.泛化关系(extends)
在这里插入图片描述
d.接口与实现关系
在这里插入图片描述

7.迪米特法则:用于降低系统的耦合度,使类与类之间保持松散的耦合关系;一个软件实体应该尽可能少的与其它实体发生相互作用。
三、简单工厂(也叫静态工厂的模式)
简单工厂不是23中设计模式一员但是可以帮助我们入门学习设计模式的思想,提供参考的源码如下:

1.目录结构,注意properties文件的位置(src下面)

在这里插入图片描述
在这里插入图片描述

//电视类:抽象的产品
package com.my.factory2;
/*
 * 抽象成品类TV
 */
public interface TV {
     //抽象方法,播放功能
	 void play();
}
//具体TV产品
package com.my.factory2;
/*
 * 具体的电视类(产品类)
 */
public class HaierTV implements TV {
	@Override
	public void play() {
		System.out.println("HaierTV的播放");
	}

}
package com.my.factory2;
/*
 * 具体的产品类
 */
public class HisenseTV implements TV {
	@Override
	public void play() {
		System.out.println(" HisenseTV的播放");
	}
}
//简单工厂类
package com.my.factory2;
/*
 * 电视机TV的工厂类,专门造电视的,包含各种品牌
 */
public class TVFactory {
    
	/**
	 * //这是简单工厂的实现,也叫静态工厂方法,它是建造型设计模式
	 * //静态的工厂方法,可以用类名直接调用
	 * 
	 * 当我们要加入新的产品时,我们就要修改下面的工厂代码,再加入else if分支,这样如果要加很多的新产品时,则会出现大量的判断语句,
	 * 代码冗余太多。而且会违背开闭原则,所以我们提出工厂方法的建造型设计模式
	 * @param brandName 表示的是传入的TV品牌,如:HaierTV,xiaomiTV
	 * @return 返回TV(接口)的实例
	 * @throws Exception 这个方法在没有对应的品牌电视时,会抛异常
	 */
	public static TV produceTVFactory(String brandName) throws Exception{
		//这表示的是忽略字母的大小写
		if("HaierTV".equalsIgnoreCase(brandName)) {
			//如果的HaierTV电视
			return new HaierTV();
		}else if("HisenseTV".equalsIgnoreCase(brandName)) {
			//如果的HisenseTV电视
			return new HisenseTV();
		}else {
			//无法生产此电视,抛异常提示,这种要配合throws使用或者try....catch...使用
			throw new Exception("对不起,该工厂暂时不能生产该品牌的电视!");
		}
	}
}

//简单工厂的测试类
package com.my.factory2;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;

public class TVFactoryTest {
	public static void main(String[] args) throws FileNotFoundException, IOException {
		//通过properties文件决定当前要制造的电视机是什么
		Properties proc=new Properties();//Properties是线程安全的
		proc.load(new BufferedInputStream(new FileInputStream("TVBrandName.properties")));//注意配置文件放在src下面,也就是类路径下
		TV tv;
		//用TVFactory类名直接调用方法,因为是静态的方法
		try {
			tv=TVFactory.produceTVFactory(proc.getProperty("brandTVName"));
			tv.play();
		} catch (Exception e) {
			System.out.println(e.getMessage());//打印异常的信息
		}
	}
}

TVBrandName.properties文件内容

#brandTVName=HaierTV
brandTVName=HisenseTV
由于简单工厂在设计上,工厂类(TVFactory )承担了创建产品对象的功能,具体产品(HaierTV)与工厂类耦合度太高;如果要创建新的产品,则需要修改TVFactory类里面的逻辑,违背了“开闭原则”,不利于我们进行扩展,所以我们可以使用“工厂方法”来进行改进,参考代码如下:
四、工厂方法(也叫工厂模式)

1.目录结构,注意properties文件(或者xml文件)的位置(src下面)
在这里插入图片描述
TVFactoryName.properties文件

#注意传递的是全路径类名否则会报ClassNotFoundException,井号表示的是注释,可以去掉他们进行测试,无需修改源代码
#TVFactoryName=com.my.HaierTVFactory
TVFactoryName=com.my.XiaomiTVFactory
#TVFactoryName=com.my.TCLTVFactory
#如果要加新的产品的话,我们只需要添加新的产品类(如:TCLTV)与对应产品的工厂(TCLTVFactory),不用修改原来的代码
//抽象产品
package com.my;
/*
抽象的产品类TV
 */
public interface TV {
    //电视都有的播放的方法
    void play();
}
//具体产品
/*
 具体的电视产品Haier电视
 */
public class HaierTV  implements TV{
    @Override
    public void play() {
        System.out.println("Haier电视的播放方法");
    }
}
/*
具体电视产品XiaomiTV
 */
public class XiaomiTV implements  TV{
    @Override
    public void play() {
        System.out.println("小米电视的播放方法");
    }
}
public class TCLTV  implements TV{
    @Override
    public void play() {
        System.out.println("TCL电视播放");
    }
}

//抽象电视工厂
/*
抽象的电视工厂,里面有抽象的造各种电视的方法
 */
public interface TVFactory {
    //返回一个抽象的接口TV
    TV produceTV();
}
//具体产品工厂
/*
具体的产品工厂,HaierTV工厂
 */
public class HaierTVFactory implements TVFactory{
    @Override
    public TV produceTV() {
        System.out.println("HaierTV工厂制造HaierTV");
        return new HaierTV();//制造的是Haier的电视
    }
}
public class TCLTVFactory implements TVFactory {
    @Override
    public TV produceTV() {
        System.out.println("TCL电视工厂制造TCL电视");
        return new TCLTV();
    }
}
//具体的产品工厂,小米电视工厂
public class XiaomiTVFactory implements TVFactory{
    @Override
    public TV produceTV() {
        System.out.println("Xiaomi电视工厂制造出Xiaomi电视");
        return new XiaomiTV();//返回的是具体小米电视对象
    }
}

//工厂方法的测试类
public class FactoryTest {
    public static void main(String[] args) throws Exception {
        //制造小米的电视
        TV tv;//接收对应的具体电视对象
        //1.准备造小米电视的工厂
        TVFactory factory=(TVFactory)Utils.getBean();
        //如果我们不加properties文件的话,我们需要new出来每一个具体的电视机工厂,现在我们需要什么电视的话,只需要修改properties文件即可
        //无序修改源代码,很好的体现了"开闭原则"
//        TVFactory factory2=new HaierTVFactory();//Haier电视工厂
        //2.调用方法制造小米电视
          tv=factory.produceTV();
        //3.调用对应的小米电视的播放方法
          tv.play();
    }
}
//工具类Utils类
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.Properties;

public class Utils {

    public static Object getBean() throws Exception{

        Properties properties=new Properties();//它是线程安全的
        //注意文件的路径是在类路径下
        properties.load(new BufferedInputStream(new FileInputStream(new File("TVFactoryName.properties"))));
        String value=properties.getProperty("TVFactoryName");//通过properties文件的键名取值
        //通过反射的方式创建类的对象,下面是包的全限定名,forName里面
        Class c1=Class.forName(value);
        Object object=c1.newInstance();
        return object;
    }

}
工厂模式的话,如果要想增加新的产品的话,那么我们需要增加一个新的具体产品(HaierTV),实现TV接口;再增加一个与之对应的HaierTVFactory,最后修改properties文件内容即可;如果有很多的新产品的话,系统中的类将会成对的增加(如:具体产品+对应的具体工厂),增加了系统的复杂度,有许多的类需要编译和运行,会带来额外的开销。而且每个产品对应的工厂(如:HaierTVFactory)它只负责生产一种具体的产品(HaierTV),不符合现实,所以提出“抽象工厂”的设计模式。参考代码如下:
四、抽象工厂方法

1.目录结构
在这里插入图片描述
FactoryName.properties文件

#注意传递的是全路径类名否则会报ClassNotFoundException,井号表示的是注释,可以去掉他们进行测试,无需修改源代码
#FactoryName=com.my.factory.HaierFactory
#FactoryName=com.my.factory.XiaomiFactory
FactoryName=com.my.factory.TCLFactory
#如果要加新的产品的话,我们只需要添加新的产品工厂(TCL工厂)与新的产品(TCL电视与空调),不用修改原来的代码
//抽象产品
package com.my.factory;
//抽象产品TV
public interface TV {
    public abstract void play();//抽象方法
}
//具体产品
//具体的产品,TCL电视
public class TCLTV implements TV{
    @Override
    public void play() {
        System.out.println("TCL电视正在播放");
    }
}
//具体的产品XiaomiTV
public class XiaomiTV implements TV {
    @Override
    public void play() {
        System.out.println("Xiaomi电视正在播放");
    }
}

//具体的产品HaierTV
public class HaierTV implements TV{
    @Override
    public void play() {
        System.out.println("HaierTV正在播放");
    }
}
//另一种抽象的产品AirConditioner
package com.my.factory;
//抽象产品空调
public interface AirConditioner {
    //抽象方法:空调调节温度的方法
    public abstract void changeTemperature();
}
//具体的产品
//具体的产品:HaierAirConditioner
public class HaierAirConditioner implements AirConditioner {
    @Override
    public void changeTemperature() {
        System.out.println("Haier空调正在改变温度");
    }
}
//TCL的空调
public class TCLAirConditioner implements AirConditioner {
    @Override
    public void changeTemperature() {
        System.out.println("TCL的空调正在改变温度");
    }
}
//具体的产品:xiaomi空调
public class XiaomiAirConditioner implements AirConditioner {
    @Override
    public void changeTemperature() {
        System.out.println("Xiaomi空调正在改变温度");
    }
}
//抽象的电器工厂
//抽象的电器工厂,可以生产很多的产品(电视,空调),不像是工厂方法里面,一个工厂就只能生产一种产品,不符合现实,所以用抽象工厂来做
public interface EFactory {
    //电器工厂造电视的方法
    public abstract TV produceTV();
    //电器工厂造空调的方法
    public abstract AirConditioner produceAirConditioner();
}
//具体的工厂
//具体的产品工厂,Haier工厂,生产Haier电视、Haier空调
public class HaierFactory implements EFactory {
    @Override
    public TV produceTV() {
        System.out.println("Haier工厂生产出Haier电视");
        return new HaierTV();
    }

    @Override
    public AirConditioner produceAirConditioner() {
        System.out.println("Haier工厂生产出Haier空调");
        return new HaierAirConditioner();
    }
}
//TCL的工厂,制造电视与空调
public class TCLFactory  implements EFactory{
    @Override
    public TV produceTV() {
        System.out.println("TCL工厂生产出TCL电视");
        return new TCLTV();
    }

    @Override
    public AirConditioner produceAirConditioner() {
        System.out.println("TCL工厂生产出TCL空调");
        return new TCLAirConditioner();
    }
}

//具体的产品xiaomi工厂,Xiaomi工厂,造xiaomi电视、空调
public class XiaomiFactory implements EFactory{

    @Override
    public TV produceTV() {
        System.out.println("小米工厂造出了Xiaomi电视");
        return new XiaomiTV();
    }

    @Override
    public AirConditioner produceAirConditioner() {
        System.out.println("小米工厂造出了Xiaomi空调");
        return new XiaomiAirConditioner();
    }
}
//抽象工厂测试类
//抽象工厂测试类
public class AbstractFactoryTest {
    public static void main(String[] args) throws Exception{
        //我们想要小米的产品,先要由一个小米制造的工厂来给我们造相关的产品
        //1.准备小米的工厂,造小米的产品
        EFactory eFactory=(EFactory)FactoryNameUtils.getBean();// 相当于EFactory eFactory=new HaierFactory();
        //1.1造小米的电视
//        TV xiaomiTV=eFactory.produceTV();
        //1.2造小米的空调
//        AirConditioner xiaomiAirConditioner=eFactory.produceAirConditioner();
        //2调用小米产品的对应的业务方法
//        xiaomiTV.play();
//        xiaomiAirConditioner.changeTemperature();

        //根据工具类来早Haier的产品
//        TV haierTV=eFactory.produceTV();//造Haier的电视
//        AirConditioner haierAirConditioner=eFactory.produceAirConditioner();//造Haier的空调
        //调用对应的产品的业务方法
//        haierTV.play();
//        haierAirConditioner.changeTemperature();

        //根据工具类来制造TCL的产品
        TV TCLTV=eFactory.produceTV();
        AirConditioner TCLAirConditioner=eFactory.produceAirConditioner();
        //调用对应的业务方法
        TCLTV.play();
        TCLAirConditioner.changeTemperature();
    }
}
//工具类
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.util.Properties;

//抽象工厂使用的读取具体工厂的工具类方法
public class FactoryNameUtils {
   public static Object getBean() throws Exception{
       Properties properties=new Properties();
       properties.load(new BufferedInputStream(new FileInputStream("FactoryName.properties")));
       String factoryName=properties.getProperty("FactoryName");//通过properties文件的键获取值
       //用反射的反射创建对象
       Object object= Class.forName(factoryName).newInstance();
       return object;
   }
}
有关产品等级结构与产品族的概念

在这里插入图片描述

;