Java常用的设计模式:简单工厂

    简单工厂不属于标准的设计模式,但是它非常的实用和简单,而且在一些思想上与标准的设计模式切合,所以暂且归为设计模式中。

一、场景问题

1.1接口回顾

    接口是一种特殊的抽象类,与抽象类相比,接口中的所有方法都是抽象方法,所有的属性都是常量,也就是接口中只有方法的定义而没有任何方法的实现。
    通常使用的接口的目的,是定义一个实现类的外观,用来约束实现类的行为。接口相当于一份契约,根据外部应用需求的功能而约定了实现类要实现的功能。当然具体的实现类可以实现接口约定的行为之外的一些行为,也就是说实现类的功能包含但不局限于接口约束的行为。通过使用接口可以实现不相关类的相同行为,而不需要考虑这些不相干类之间的关系。
    接口的设计思想就是“ 封装隔离”,通常所指的封装是对数据域的封装,而这里所指的封装是指被隔离体实现的封装。隔离指的是对外部应用调用和内部方法实现的隔离。外部调用只能通过接口来调用方法,而不知道具体的实现。也就是说外部调用和具体实现是被接口隔离开的。
    由于外部调用和内部实现被接口隔离,所以只要接口不发生变化,内部具体的实现的改变不会影响到外部的应用。从而使得系统变得更加的灵活,具有更好的可扩展性和维护性。
    既然接口是一种特殊的抽象类,那么在具体的使用中,如何选择接口还是抽象类呢?有以下两个总结:
    1.优先选用接口
    2.如果既要定义子类的行为,又要为子类提供公共的功能时,那么应该选择抽象类。

1.2面向接口编程

    Java程序设计中非常讲究层次和模块的划分,通常按照三层来划分Java程序,分别是表现层、逻辑层、数据层。它们之间的交互都是通过接口实现的。同时,在每一个层中,又分为多个独立的模块,每个小模块对外也是一个整体,因此在调用交互的时候也需要相应的接口。这也就是前边所说的,接口是其被隔离部分的外观。基本的三层结构如下所示:
    
在其中某一个层内部的组件交互也是通过接口完成,各个接口内部如何定义,这要根据具体的需求来确定。我们可以看到不管是一个层还是一个模块还是一个组件,都是一个被接口隔离的整体,因此我们实际上可以不用区分组件或者是模块,而统统的认为它们是被接口隔离即可。接口示意图如下:
    
那么如何面向接口编程呢?

1.3不用模式的解决方案

    在不用设计模式的情况下使用接口,我们假设有个接口名字为Api,这个接口对应一个实现类Impl,那么客户端如何使用这个接口呢?通常情况下是在客户端创建一个Impl实例,然后把它赋值给一个Api接口类型的变量,然后客户端就可以通过这个变量来操作接口的功能了。此时具体的结构图如下:
    
代码如下:
首先定义一个接口Api.java
package com.chenxyt.java.practice;
/*
 * 某个接口(不具有业务实际意义)
 */
public interface Api {
	/*
	 * 一个测试方法 功能将传入的s打出
	 * @param s 
	 */
	public void test1(String s);
}

然后是这个接口的实现类Impl.java

package com.chenxyt.java.practice;
public class Impl implements Api{
	@Override
	public void test1(String s) {
		// TODO Auto-generated method stub
		System.out.println(s);
	}

}

最后是一个客户端类Client.java,使用上边定义的接口

package com.chenxyt.java.practice;
public class Client {
	public static void main(String[] args) {
		Api api = new Impl();
		api.test1("Hello API");
	}
}

运行结果:

    

1.4有何问题

    上述代码表面上看没有什么问题,正常的业务逻辑也都是这样写的。但是实际上这样做没有符合Java接口的设计理念。隔离客户端与实现。
Api api = new Impl();
这里在创建实现类的实例的时候,还是把实现类暴露给客户端了,并没有做到完全的隔离。那么如果把new Impl去掉的话又无法正确得到接口的对象了。这个问题描述一下就是,在Java编程中,只知道接口,不知道实现该怎么办。

二、解决方案

2.1使用简单工厂来解决

     简单工厂的概念:提供一个用来创建对象实例的功能,而不需要去关心具体的实现。被创建实例的类型可以是接口、抽象类也可以是一个具体的类。生活中的例子,大概就是有一个生产衣服的工厂,你只管去拿即可,不需要管衣服是怎么做的,也不需要让你自己做衣服了,衣服有各种各样的款式和大小,肯定会符合你的品位。

    分析上边的问题,我们的目的就是不让外部模块知道具体的实现,也就是创建接口的示例的时候,不能使用接口的实现进行创建了。因此我们就把创建实例的方法转交给另一个类,这个类可以定义在模块内部,它可以知道也必须知道实例的具体实现。我们称这个类为Factory类。整理一下实现的思路就是我们创建一个Factory类,在这个类的内部来创建接口的实例,客户端通过Factory获得接口的实例,进而实现接口的方法。

2.2简单工厂结构说明

     

    图中前边示例的实现方式是Client通过创建Api的实例Impl1或者Impl2实现operation功能,使用了设计模式之后的实现方式是创建一个工厂类,客户端调用这个工厂类来实现创建Api接口的实例Impl1或者Impl2,从而实现了真正的面向接口编程,隐藏了接口Api的实现部分Impl1或者Impl2

2.3简单工厂示例代码

 首先是我们要实现的接口Api

package com.chenxyt.java.practice;

/**
 * 一个要被调用的接口,该接口可以通过简单工厂创建
 * @author Crayon
 *
 */
public interface Api {
	
	/**
	 * 接口内定义的方法示例
	 * @param s
	 */
	public void operation(String s);
}

接下来是接口Api的两个实现

package com.chenxyt.java.practice;
public class Impl implements Api{
	@Override
	public void operation(String s) {
		// TODO Auto-generated method stub
		System.out.println(s);
	}
}

接下来是核心部分,也就是简单工厂的工厂Factory实现

package com.chenxyt.java.practice;
public class Factory{
	public static Api createApi(int condition){
		Api api = null;
		if(condition == 1){
			api = new Impl();
		}else if(condition == 2){
			api = new Impl();
		}
		return api;
	}
}

这里只是做了一个示例,有多个实例的时候可以增加condition选择条件,由客户端选择创建哪个。类似在工厂买衣服,我给了你指定的大小尺码标准。

最后是客户端的调用,就是通过简单的静态方法的形式进行调用。

package com.chenxyt.java.practice;
public class Client {
	public static void main(String[] args) {
		Api api = Factory.createApi(1);
		api.operation("Hello");
	}
}

2.4使用简单工厂重写示例

    这里上边的示例代码即是文章开始的示例部分的简单工厂模式。

三、模式讲解

 3.1典型疑问

    从上边的示例可以看出,所谓的简单工厂不过是将new Impl()放在了Factory类中,也就是它其实只是换了个位置,不是吗?这个问题的关键在于面向对象的封装特性,我们将Factory封装在了封装体内部,也就是Factory与Client是完全隔离的,因为换了个位置但是本质是发生了变化的。

3.2认识简单工厂

     所谓工厂就是用来生产东西的一个场所,这里简单工厂设计模式不仅可以创建接口还可以创建抽象类与普通的实体类,因此也是一种万能工厂。同时我们也把简单工厂可以称作为是一种静态工厂,因为我们没有创建工厂实例的必要,仅仅需要将该类的方法设置为static的即可。为了防止客户端无意中将工厂类实例化,我们也可以像单例模式那样将构造函数私有化。虽然简单工厂理论上可以创建很大的范围,但是实际开发中为了更好的体现软件设计的模块化与结构化,建议工厂只创建本模块内的内容。

3.3简单工厂中方法的写法

     继续理解上边的Factory类,发现它虽然能够创建接口、抽象类、实体类这些,但是真正实现这些功能的,还是他们具体的实现。(比如Impl)也就是说,简单工厂实际上就是一个实现了选择的工具,选择要实现什么功能。因此大多数情况下,Facotry内部的方法都是根据指定条件做选择。条件可以用形参传入,也可以从配置文件获取等各种形式。看到这里,可能前边说的衣服加工厂的例子稍微有一点不合适,大概像是一个中介吧,你告诉我你要什么,我给你选择,帮你实现,但是实现这一块不是我中介来做。

3.4可配置的简单工厂

     结合配置文件和反射,可以实现简单工厂的配置化,当然使用IOC也可以实现。这里暂时介绍一下使用反射的方式配置。主要思想就是配置文件中配置要在工厂方法里创建的类的全名,然后通过反射的方式动态加载这个类,以达到目的。

示例的配置文件factory.properties如下:

ImplClass=com.chenxyt.practice.ImplClass1 

如上配置文件中我们定义了一个全新的类的名称(包含包路径的名称)

接下来重新编写Factory类

package com.chenxyt.java.practice;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Factory{
	public static Api createApi(){
		Properties p = new Properties();
		InputStream in = null;
		try{
			//装载配置文件
			in = Factory.class.getResourceAsStream("factory.properties");
			p.load(in);;
		}catch(IOException e){
			e.printStackTrace();
		}finally{
			try{
				in.close();
			}catch(IOException e){
				e.printStackTrace();
			}
		}
		Api api = null;
		try{
			//使用反射创建配置文件中定义的类
			api = (Api)Class.forName(p.getProperty("ImplClass")).newInstance();
		}catch(Exception e){
			e.printStackTrace();
		}
	}
}

3.5简单工厂的优缺点

    优点:帮助封装,有助于面向接口编程,实现了客户端与接口之间的解耦。

    缺点:违背了面向对象思想中的“开放-关闭”规则,一旦新增需要工厂创建的产品,需要对工厂类进行修改。静态方法不能被继承重写,因此无法实现继承结构。工厂类的本质是选择实现,因此当工厂类不可使用时,整个系统将受到影响。

3.6思考简单工厂

    简单工厂的实质就是选择实现,当我们需要隔离封装的时候建议使用该模式,当我们想把创建外部对象的方法集中管理的时候,可以选择该模式。

3.7相关设计模式

    相关的还有抽象工厂设计模式、工厂方法设计模式。如文章开篇所述,简单工厂与其它模式相比不属于标准的实际模式。相关的设计模式待续。

四、总结

    为了便于整理全文以及后续复习,增加一个概括的章节。简单工厂说到底就是一个用来实现创建接口或者抽象的类的类,它的本质思想是选择实现,可以通过配置文件、反射、IOC等方式实现配置化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值