一天一模式之2简单工厂

				原博文:https://blog.csdn.net/KongZhongNiao/article/details/79558891					

提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类。

结构和说明

image
- Api:定义客户所需要的功能接口
- Impl:具体实现Api的实现类,可能会有多个
- Factory:工厂,选择合适的实现类来创建Api接口对象
- Client:客户端,通过Factory去获取Api接口对象,然后面向Api接口编程

接口回顾

1.Java中接口的概念在Java中接口是一种特殊的抽象类
2. 接口用来干什么通常用接口来定义实现类的外观,就相当于一份契约,根据外部应用需要的功能,约定了实现类应该要实现的功能
3. 接口的思想——“封装隔离”
4. 使用接口的好处只要接口不变,内部实现的变化就不会影响到外部应用,从而使得系统更灵活,具有更好的扩展性和可维护性
5. 接口和抽象类的选择(1)优先选用接口(2)在如下情况选抽象类:既要定义子类的行为,又要为子类提供公共的功能

java实例

定义接口api

package cn.javass.dp.simplefactory.example2;
/**
 * 接口的定义,该接口可以通过简单工厂来创建
 */
public interface Api {
    /**
     * 示意,具体的功能方法的定义
     * @param s 示意,需要的参数
     */
    public void operation(String s);
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

实现接口实例A

package cn.javass.dp.simplefactory.example2;
/**
 * 接口的具体实现对象A 
 */
public class ImplA implements Api{
    public void operation(String s) {
        //实现功能的代码,示意一下
        System.out.println("ImplA s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

实现接口实例B

package cn.javass.dp.simplefactory.example2;
/**
 * 接口的具体实现对象B 
 */
public class ImplB implements Api{
    public void operation(String s) {
        //实现功能的代码,示意一下
        System.out.println("ImplB s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

定义工厂

package cn.javass.dp.simplefactory.example2;
/**
 * 工厂类,用来创造Api对象
 */
public class Factory {
    /**
     * 具体的创造Api对象的方法
     * @param condition 示意,从外部传入的选择条件
     * @return 创造好的Api对象
     */
    public static Api createApi(int condition){
        //应该根据某些条件去选择究竟创建哪一个具体的实现对象
        //这些条件可以从外部传入,也可以从其它途径获取
        //如果只有一个实现,可以省略条件,因为没有选择的必要

        //示意使用条件
        Api api = null;
        if(condition == 1){
            api = new ImplA();
        }else if(condition == 2){
            api = new ImplB();
        }
        return api;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

客户端

package cn.javass.dp.simplefactory.example2;
/**
 * 客户端,使用Api接口
 */
public class Client {
    public static void main(String[] args) {
        //通过简单工厂来获取接口对象
        Api api = Factory.createApi(1);


        api.operation("正在使用简单工厂");
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

面向接口编程

面向接口编程是Java编程中的一个重要原则。在Java 程序设计里面,非常讲究层的划分和模块的划分。比如常见的三层结构:
image

在一个层内部的各个模块交互也要通过接口

image

不管是一层还是一个模块或者一个组件,都是一个被接口隔离的整体

image

问题

既然在Java中需要面向接口编程,那么在程序中到底如何使用接口,来做到真正的面向接口编程呢?

不用模式的解决方案

image

java实例
接口api
package cn.javass.dp.simplefactory.example1;
/**
 * 某个接口(通用的、抽象的、非具体的功能的) 
 */
public interface Api {
    /**
     * 某个具体的功能方法的定义,用test1来演示一下。
     * 这里的功能很简单,把传入的s打印输出即可 
     * @param s 任意想要打印输出的字符串
     */
    public void test1(String s);
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
接口实现类
package cn.javass.dp.simplefactory.example1;
/**
 * 对接口的实现 
 */
public class Impl implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl. The input s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
客户端
package cn.javass.dp.simplefactory.example1;
/**
 * 客户端:测试使用Api接口
 */
public class Client {
    public static void main(String[] args) {
        Api api = new Impl();


        api.test1("哈哈,不要紧张,只是个测试而已!");
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这是真的面向接口编程吗?
这既没有没有把实现和接口封装,也没有实现隔离。

有何问题?

把这个问题描述一下:在Java编程中,出现只知接口而不知实现,该怎么办?

使用模式的解决方案

image

java实例
接口api
package cn.javass.dp.simplefactory.example3;
/**
 * 某个接口(通用的、抽象的、非具体的功能的) 
 */
public interface Api {
    /**
     * 某个具体的功能方法的定义,用test1来演示一下。
     * 这里的功能很简单,把传入的s打印输出即可 
     * @param s 任意想要打印输出的字符串
     */
    public void test1(String s);
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
接口实现
package cn.javass.dp.simplefactory.example3;
/**
 * 对接口的实现 
 */
public class Impl implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl. The input s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
工厂
package cn.javass.dp.simplefactory.example3;
/**
 * 工厂类,用来创造Api对象
 */
public class Factory {
    private Factory(){

    }
    /**
     * 具体的创造Api对象的方法
     * @return 创造好的Api对象
     */
    public static Api createApi(){
        //主要用来实现  选择合适的实现类  来创建实例对象


        //由于只有一个实现,就不用条件判断了
        return new Impl();
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
客户端
package cn.javass.dp.simplefactory.example3;
/**
 * 客户端:测试使用Api接口
 */
public class Client {
    public static void main(String[] args) {
        //重要改变,没有new Impl()了,取而代之Factory.createApi()
        Api api = Factory.createApi();


        api.test1("哈哈,不要紧张,只是个测试而已!");
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

使用简单工厂,就可以实现接口和实现的封装和隔离,客户端只能操作接口的api。

理解简单工厂

一个典型的疑问

首先来解决一个常见的疑问:可能有朋友会认为,上面示例中的简单工厂看起来不就是把客户端里面的“new Impl()”移动到简单工厂里面吗?不还是一样通过new一个实现类来得到接口吗?把“new Impl()”这句话放到客户端和放到简单工厂里面有什么不同吗?

理解这个问题的重点就在于理解简单工厂所处的位置。

image

认识简单工厂

  1. 简单工厂的功能
    可以用来创建的接口、抽象类或者是普通类的实例

  2. 静态工厂
    通常把简单工厂类实现成一个工具类,直接使用静态方法就可以了,也就是说简单工厂的方法通常都是静态的,所以也被称为静态工厂

  3. 万能工厂
    一个简单工厂理论上可以用来构造任何对象,所以又称之为“万能工厂”

  4. 简单工厂创建对象的范围
    建议控制在一个独立的组件级别或者一个模块级别

  5. 简单工厂的调用顺序示意图
    image

  6. 简单工厂命名的建议

  • (1)类名建议为“模块名称+Factory”,比如:用户模块的工厂就称为:UserFactory
  • (2)方法名称通常为“get+接口名称”或者是“create+接口名称”

- (3)不建议把方法名称命名为“new+接口名称”

简单工厂中方法的写法

简单工厂方法的内部主要实现的功能是“选择合适的实现类”来创建实例对象。

注意:如果是从客户端在调用工厂的时候,传入选择的参数,这就说明客户端必须知道每个参数的含义,也需要理解每个参数对应的功能处理。这就要求必须在一定程度上,向客户暴露一定的内部实现细节。

可配置的简单工厂

使用反射加上配置文件,来实现添加新的实现类过后,无须修改代码,就能把这个新的实现类加入应用中

选择

1:参数来源于 client

接口
package cn.javass.dp.simplefactory.example4;
/**
 * 某个接口(通用的、抽象的、非具体的功能的) 
 */
public interface Api {
    /**
     * 某个具体的功能方法的定义,用test1来演示一下。
     * 这里的功能很简单,把传入的s打印输出即可 
     * @param s 任意想要打印输出的字符串
     */
    public void test1(String s);
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
实现1
package cn.javass.dp.simplefactory.example4;
/**
 * 对某个接口的一种实现 
 */
public class Impl implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl. The input s=="+s);
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
实现2
package cn.javass.dp.simplefactory.example4;
/**
 * 对接口的一种实现 
 */
public class Impl2 implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl2. The input s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
工厂
package cn.javass.dp.simplefactory.example4;

/**
 * 工厂类,用来创造Api的
 */
public class Factory {
    /**
     * 具体的创造Api的方法,根据客户端的参数来创建接口
     * @param type 客户端传入的选择创造接口的条件
     * @return 创造好的Api对象
     */
    public static Api createApi(int type){
        //这里的type也可以不由外部传入,而是直接读取配置文件来获取
        //为了把注意力放在模式本身上,这里就不去写读取配置文件的代码了

        //根据type来进行选择,当然这里的1和2应该做成常量


        //选择===〉如何选?====〉选择的参数===〉参数从何而来?
        //1:参数来源于 client
        //2:参数来源于配置文件
        //3:参数来源于系统自身,比如运行期间的某个值

        Api api = null;
        if(type==1){
            api = new Impl();
        }else if(type==2){
            api = new Impl2();
        }
        return api;
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

2:参数来源于配置文件

接口
package cn.javass.dp.simplefactory.example5;
/**
 * 某个接口(通用的、抽象的、非具体的功能的) 
 */
public interface Api {
    /**
     * 某个具体的功能方法的定义,用test1来演示一下。
     * 这里的功能很简单,把传入的s打印输出即可 
     * @param s 任意想要打印输出的字符串
     */
    public void test1(String s);
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
实现1
package cn.javass.dp.simplefactory.example5;
/**
 * 对某个接口的一种实现 
 */
public class Impl implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl. The input s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
实现2
package cn.javass.dp.simplefactory.example5;
/**
 * 对某个接口的一种实现 
 */
public class Impl2 implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl222222. The input s=="+s);
    }
}



 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
实现3
package cn.javass.dp.simplefactory.example5;

public class Impl3 implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl33333. The input s=="+s);
    }
}





 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
配置文件FactoryTest.properties
ImplClass=cn.javass.dp.simplefactory.example5.Impl3





 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
工厂
package cn.javass.dp.simplefactory.example5;
import java.util.*;
import java.io.*;
/**
 * 工厂类,用来创造Api对象
 */
public class Factory {
    /**
     * 具体的创造Api的方法,根据配置文件的参数来创建接口
     * @return 创造好的Api对象
     */
    public static Api createApi(){
        //直接读取配置文件来获取需要创建实例的类

        //至于如何读取Properties还有如何反射这里就不解释了
        Properties p = new Properties(); 
        InputStream in = null;
        try {
            in = Factory.class.getResourceAsStream("FactoryTest.properties");
            p.load(in);
        } catch (IOException e) {
            System.out.println("装载工厂配置文件出错了,具体的堆栈信息如下:");
            e.printStackTrace();
        }finally{
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //用反射去创建,那些例外处理等完善的工作这里就不做了
        Api api = null;
        try {
            api = (Api)Class.forName(p.getProperty("ImplClass")).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return api;
    }
}




 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
客户端
package cn.javass.dp.simplefactory.example5;

/**
 * 客户端:测试使用Api接口
 */
public class Client {
    public static void main(String[] args) {
        //重要改变,没有new Impl()了,取而代之Factory.createApi()

        Api api = Factory.createApi();

        api.test1("哈哈,不要紧张,只是个测试而已!");
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3:参数来源于系统自身,比如运行期间的某个值

接口
package cn.javass.dp.simplefactory.example6;
/**
 * 某个接口(通用的、抽象的、非具体的功能的) 
 */
public interface Api {
    /**
     * 某个具体的功能方法的定义,用test1来演示一下。
     * 这里的功能很简单,把传入的s打印输出即可 
     * @param s 任意想要打印输出的字符串
     */
    public void test1(String s);
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
实现1
package cn.javass.dp.simplefactory.example6;
/**
 * 对某个接口的一种实现 
 */
public class Impl implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl. The input s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
实现2
package cn.javass.dp.simplefactory.example6;
/**
 * 对接口的一种实现 
 */
public class Impl2 implements Api{

    public void test1(String s) {
        System.out.println("Now In Impl2. The input s=="+s);
    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
工厂
package cn.javass.dp.simplefactory.example6;

/**
 * 工厂类,用来创造Api的
 */
public class Factory {
    private static int count = 0;

    /**
     * 具体的创造Api的方法,根据客户端的参数来创建接口
     * @param type 客户端传入的选择创造接口的条件
     * @return 创造好的Api对象
     */
    public static Api createApi(){
        //这里的type也可以不由外部传入,而是直接读取配置文件来获取
        //为了把注意力放在模式本身上,这里就不去写读取配置文件的代码了

        //根据type来进行选择,当然这里的1和2应该做成常量


        //选择===〉如何选?====〉选择的参数===〉参数从何而来?
        //1:参数来源于 client
        //2:参数来源于配置文件
        //3:参数来源于系统自身,比如运行期间的某个值

        Api api = null;
        if(count<3){
            api = new Impl();
            count++;
        }else{
            api = new Impl2();
            count++;
        }
        return api;
    }
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
客户端
package cn.javass.dp.simplefactory.example6;
/**
 * 客户端:测试使用Api接口
 */
public class Client {
    public static void main(String[] args) {
        //重要改变,没有new Impl()了,取而代之Factory.createApi()
        //注意这里传递的参数,修改参数就可以修改行为,试试看吧

        for(int i=0;i<5;i++){
            Api api = Factory.createApi();
            api.test1("哈哈,不要紧张,只是个测试而已!");
        }

    }
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

简单工厂的优缺点

  • 1:帮助封装
  • 2:解耦
  • 3:可能增加客户端的复杂度

- 4:不方便扩展子工厂

思考简单工厂

简单工厂的本质

简单工厂的本质是:选择实现

何时选用简单工厂

  1. 如果想要完全封装隔离具体实现,让外部只能通过接口来操作封装体,那么可以选用简单工厂,让客户端通过工厂来获取相应的接口,而无需关心具体实现
  2. 如果想要把对外创建对象的职责集中管理和控制,可以选用简单工厂,一个简单工厂可以创建很多的、不相关的对象,可以把对外创建对象的职责集中到一个简单工厂来,从而实现集中管理和控制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Famiglistimott

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值