工厂之OPC问题

本文探讨了在软件开发中如何通过改进工厂设计模式来实现对需求的开放性和封闭性的更好遵循,详细解释了简单工厂、工厂方法、工厂方法结合反射等解决方案,并通过配置文件动态获取工厂类实例,确保了系统的灵活性和可维护性。
摘要由CSDN通过智能技术生成

一、简单工厂,违背了(OCP).


	
 
public class TVFactory {
public static final String Hai_Er="海尔";
public static final String CHANG_HONG="长虹";
publicstatic TV createTV(String type){
TVtv=null;
if(Hai_Er.equals(type)){
tv=newHaiErTV();                
}elseif(CHANG_HONG.equals(type)){
tv=newChangHongTV();
}else{
throw new RuntimeException("无法产生!");
}
returntv;
}                
}
 

public interface TV {
public void turnOn();//打开电视
public void turnOff();//关闭电视
}
 
public class HaiErTV implements TV {
 
@Override
publicvoid turnOn() {
//TODO Auto-generated method stub
System.out.println("HaiErTV.turnOn()");
}
 
@Override
publicvoid turnOff() {
//TODO Auto-generated method stub
System.out.println("HaiErTV.turnOff()");
 
}
 
}
 
 
public class ChangHongTV implements TV {
 
@Override
publicvoid turnOn() {
System.out.println("ChangHongTV.turnOn()");
 
 
}
 
@Override
publicvoid turnOff() {
 
System.out.println("ChangHongTV.turnOff()");
}
 
}
 
 
public class Client {
 
/**
 * 函数功能说明
 * huxj 2014-10-1
 * @参数: @param args   
 * @return void  
 * @throws
 */
 
publicstatic void main(String[] args) {
TV tv=TVFactory.createTV("海尔");
 
tv.turnOn();
tv.turnOff();
}
 
}

运行结果:


简单工厂的弊端运:

现在我们需要“海信”品牌的电视的,操作如下:

1)添加一个“海信电视”类继承“电视”抽象类。

2)修改“TVFactory ”类,添加有关“海信电视”的逻辑处理。

3)客户端“Client”类中,品牌改变,代码跟着改变。

总结:简单工厂23)说明步骤严重违背了设计模式的基本原则“The Open-Closeed-Principle,  开放封闭”原则。

 

简单工厂严重违背了“开放封闭”原则,那是不是简单工厂就没有用武之地了呢?

其实不是这样的,当我们需求稳定的时候,比如我们就只需要“长虹”“海尔”两种品牌的电视机,那么我们就不需要修改“TVFactory”,这时候用简单工厂与后续要将的其他用工厂要简单很多。很多东西放到合适的地方才能发挥它自己独特的魅力。

 

下面,就上面两个问题,们就逐步引入工厂方法,反射来一一解决问题。

 

二、工厂方法:解决"需求改变,修改工厂"的问题


public  interface TVFactory{
publicTV createTV();
 
}
 
publicclass TVFactoryHaiEr implements TVFactory {
 
/**
 *
 */
@Override
publicTV createTV() {
 
returnnew HaiErTV();
}
 
}
 
 
 
publicclass TVFactoryChangHong implements TVFactory {
 
/**
 *
 */
@Override
publicTV createTV() {
 
returnnew ChangHongTV();
}
 
}
 
 
publicclass Client {
 
/**
 * 函数功能说明
 * huxj 2014-10-1
 * @参数: @param args   
 * @return void  
 * @throws
 */
 
publicstatic void main(String[] args) {
TVFactorynewFactory=new TVFactoryHaiEr();
//TVFactorynewFactory=new TVFactoryChangHong();
newFactory.createTV().turnOn();
newFactory.createTV().turnOff();
}
 
}
 


大家看UML图和代码之后应该比较容易理解:

1)解决的办法就是由父类派生出对应的多个共产(每个工厂对应该品牌的产品)这就将职责分离,符合了“单一职责”的原则。

这时,当我们需要“海信”品牌的电视的时候,只需要添加“海信电视”类,及对应的工厂即可,这样最做了添加(品牌删除同样也是),也就是说不必类内部代码,这样就遵循“OCP”原则!

2)但是要想解决客户端的问题,请看下文讲解:

 

三、工厂方法+反射,解决客户端违背“OCP”原则

public static voidmain(String[] args) {
TVFactorynewFactory=new TVFactoryHaiEr();
//TVFactorynewFactory=new TVFactoryChangHong();
newFactory.createTV().turnOn();
newFactory.createTV().turnOff();
}
 
}


关于TV所有的代码,目前只有客户端这里有变化,而且变化的地方

TVFactorynewFactory=new TVFactoryHaiEr()即实例化的工厂,其实解决此问题的思路很简单,把实例化工厂以变量的思维进行解决,通过配置文给变量赋值即可,下面就此思路展开:

 

配置工厂

sys-config.xml
<?xmlversion="1.0" encoding="UTF-8"?>
<config>
<dao-factory>
<item-dao-factory>test.TVFactoryHaiEr</item-dao-factory>
</dao-factory>
</config>

读取配置文件,返回所需的工厂类

 

/**
 * 采用单例模式解析sys-config.xml文件
 * @author Administrator
 *
 */
public classXmlConfigReader {
 
 
//懒汉式(延迟加载lazy)
privatestatic XmlConfigReader instance = null;
 
//key表示<dao-factory>标签名
//value标签中的值,具体完整路径(成员变量)
privateMap<String,String> daoFactoryMap=new HashMap<String,String>();
 
privateXmlConfigReader() {
SAXReaderreader = new SAXReader();
InputStreamin =Thread.currentThread().getContextClassLoader().getResourceAsStream("sys-config.xml");
try {
Documentdoc = reader.read(in);
//读取DaoFactory信息
ListdaoFactoryList=doc.selectNodes("/config/dao-factory/*");
//获得子节点标签值
for(inti=0;i<daoFactoryList.size();i++){
ElementdaoFactoryElt=(Element)daoFactoryList.get(i);
StringtagName=daoFactoryElt.getName();
StringtagText=daoFactoryElt.getText();
//System.out.println(tagName);
//System.out.println(daoFactoryElt.elementText("item-dao-factory"));
//System.out.println("读取的内容是:"+tagText);
 
//把取的值放入map当中
daoFactoryMap.put(tagName,tagText);
}
}catch (DocumentException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}                        
}
 
publicstatic synchronized XmlConfigReader getInstance() {
if(instance == null) {
instance= new XmlConfigReader();
}
returninstance;
}
 
/**
 * @param name
 * 返回工厂类的具体路径。
 *
 *@return value值
 *
 */
publicString getDaoFactory(String name){
returndaoFactoryMap.get(name);
}
}
 

 

客户端代码:

/**
 * @类功能说明:工厂方法+反射  解决:工厂方法违背开闭原则的问题
 * @作者:huxj
 * @创建时间:2014-10-1下午12:16:37
 * @版本:V1.0
 */
 
public class Client {
 
/**
 * 函数功能说明
 * huxj 2014-10-1
 * @参数: @param args   
 * @return void  
 * @throws
 */
 
publicstatic void main(String[] args) {
//通过配置文件获取,工厂类,解决修改代码的功能。
StringclassName=XmlConfigReader.getInstance().getDaoFactory("item-dao-factory");
//把类装载到内存里
TVFactoryfactory=null;
try {
factory= (TVFactory)Class.forName(className).newInstance();
}catch (InstantiationException e) {
e.printStackTrace();
}catch (IllegalAccessException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
factory.createTV().turnOn();
factory.createTV().turnOff();
}
 
}
 
运行结果:


 

通过变量获取StringclassName=XmlConfigReader.getInstance().getDaoFactory("item-dao-factory")工厂类,然后TVFactory factory = (TVFactory)Class.forName(className).newInstance()

实例化;从配置文件中获取变化的类,这样很好的维护了“OPC”原则,而且使更换数据库更加的灵活。

 

总结:

问题总算圆满解决,我感觉“perfect!”。静下心来,学习有很多的乐趣!

 

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值