1.定义
OCP(Open-Closed Principe):一个软件实体应该对扩展开放,对修改关闭。也就是在设计一个模块的时候,应当使这个模块可以在不被修改的前提下被扩展,即实现在不改源代码的情况下改变这个模块的行为。
2.案例
某图形界面系统提供了各种不同形状的按钮,客户端代码可针对这些按钮进行编程,用户可能会改变需求而使用不同的按钮,原始设计方案如下图所示:
3.分析
由于LoginForm类面向具体对象而进行编程,因此每次更换具体类时不得不修改源代码,而且这些类中方法都没有一个统一的接口,相似功能的方法名称不一致。如果系统要满足开闭原则,需要对按钮类进行抽象化,提取一个抽象类AbstructButton,LoginForm类针对抽象类AbstractButton进行编程,在Java中,可以通过配置文件、DOM解析技术和反射机制将具体类类名存储在配置文件中,再在生成时实例化其对象(后续介绍)。
使用抽象类AbstractButton后,如果需要矩形按钮(RectangleButton),只需添加一个新的类继承抽象类并修改配置文件OCP.xml即可,无须修改LoginForm类中的代码和AbstractButton的代码,在不修改的前提下扩展系统功能的要求,完全符合开闭原则。配置文件一般都用XmL格式文件或properities格式的属性文件,如图:
注:xml和properties的配置文件都是纯文本文件,可以直接通过VI编辑器或记事本进行编辑,且无需编译,因此在软件开发中,一般不把配置文件的修改认为是对系统源代码的修改。
4.实现
除了图中四个类,我自己写了个XMLHepler类,用来帮助LoginForm获取按钮名。
4.1XMLHelper类的设计实现如下:
package OCP; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; public class XMLHepler { //帮助loginform获取到按钮名字的类 操作xml文件 public XMLHepler() { // TODO Auto-generated constructor stub } public String getButtonName() { DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();//实例化一个documentbuilderfactory 用来获取documentbuilder DocumentBuilder builder=null; try { builder=factory.newDocumentBuilder(); }catch(ParserConfigurationException e) { e.printStackTrace(); } Document document=null; try { document=builder.parse("OCP.xml");//从指定路径获取xml文件 这里ocp.xml在项目文件夹下 }catch (SAXException e) { // TODO: handle exception e.printStackTrace(); }catch(IOException e) { e.printStackTrace(); } NodeList nList=document.getElementsByTagName("ButtonName");//节点名叫“buttonname” 获取其中的内容 return nList.item(0).getFirstChild().getNodeValue();//返回其内容 } }
4.2LoginForm类的设计实现如下:
package OCP; import java.awt.Container; import javax.swing.*; public class LoginForm extends JFrame{ private AbstructButton button=null; private XMLHepler xmlHepler=null; public LoginForm() { setTitle("OCP"); setVisible(true); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); setBounds(0,0,200,200); setLayout(null);//绝对布局 } //面板容器添加控件 public void showControl() throws InstantiationException, IllegalAccessException, ClassNotFoundException { xmlHepler=new XMLHepler(); String btnName=xmlHepler.getButtonName(); Container container=getContentPane(); button=(AbstructButton)Class.forName(btnName).newInstance();//根据名字来实例化对应的按钮 button.InitButton(); container.add(button); } public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { // TODO Auto-generated method stub new LoginForm().showControl(); } }
4.3AbstractButton类设计实现如下:
package OCP; import javax.swing.*; public abstract class AbstructButton extends JButton { //设计抽象类 满足所有按钮的需求 public AbstructButton() { // TODO Auto-generated constructor stub } public abstract void InitButton();//抽象方法没有方法体 且子类必须重写 }
4.4RectangleButton类设计实现如下:
package OCP; import java.awt.Color; public class RectangleButton extends AbstructButton { //实际按钮一 矩形按钮 public RectangleButton() { // TODO Auto-generated constructor stub } @Override public void InitButton()//重写父类方法 初始化一个矩形按钮 { this.setText("矩形按钮"); this.setBounds(50,50,120,40); this.setBackground(Color.CYAN); } }
4.5CircleButton类设计实现如下:
package OCP; import java.awt.Color; public class CircleButton extends AbstructButton{ //实际按钮二 圆形按钮 public CircleButton() { // TODO Auto-generated constructor stub } @Override public void InitButton()//重写父类方法 圆形按钮初始化 { this.setText("圆形按钮"); this.setBackground(Color.yellow); this.setBounds(50,50,120,40); } }
4.6运行效果图如下:
注:本人参考的书籍是清华大学出版社,刘伟主编的《设计模式》。代码中存在的不足,还请多多指教。