营销网站建设企划案例,jsp做新闻系统门户网站,香奈儿网站建设策划书,北京南站附近的景点系列文章目录
第一章 Java 设计模式之七大设计原则 文章目录系列文章目录前言一、单一职责原则1.案例分析2.改进二、开闭原则1.案例分析2.改进三、里氏替换原则1.案例分析2.改进四、依赖倒转原则五、接口隔离原则1.案例分析2.改进六、合成复用原则1.案例分析2.改进七、迪米特原…系列文章目录
第一章 Java 设计模式之七大设计原则 文章目录系列文章目录前言一、单一职责原则1.案例分析2.改进二、开闭原则1.案例分析2.改进三、里氏替换原则1.案例分析2.改进四、依赖倒转原则五、接口隔离原则1.案例分析2.改进六、合成复用原则1.案例分析2.改进七、迪米特原则1.案例分析2.改进总结前言
面向对象设计原则为支持可维护性复用而诞生这些原则蕴含在很多设计模式中它们是从许多设计方案中总结出的指导性原则。面向对象设计原则也是我们用于评价一个设计模式的使用效果的重要指标之一本文就记录了常见7种面向对象设计原则学习笔记。 一、单一职责原则
单一职责原则是最简单的面向对象设计原则它用于控制类的粒度大小。单一职责原则定义如下一个类只负责一个功能领域中的相应职责或者可以定义为就一个类而言应该只有一个引起它变化的原因。
一个类大到模块小到方法承担的职责越多它被复用的可能性就越小而且一个类承担的职责过多就相当于将这些职责耦合在一起当其中一个职责变化时可能会影响其他职责的运作因此要将这些职责进行分离将不同的职责封装在不同的类中即将不同的变化原因封装在不同的类中如果多个职责总是同时发生改变则可将它们封装在同一类中。
单一职责原则是实现高内聚、低耦合的指导方针它是最简单但又最难运用的原则需要设计人员发现类的不同职责并将其分离
1.案例分析
针对某客户关系管理系统中客户信息图形统计模块设计出如下方案 public class CustomerDataChart {public void getConnetion(){System.out.println(数据库连接...);}public void findCustomers(){System.out.println(查询客户信息...);}public void createChart(){System.out.println(创建图表...);}public void displayChart(){System.out.println(展示图表...);}
}
问题CustomerDataChart类承担了太多的职责既包含与数据库相关的方法又包含与图表生成和显示相关的方法。如果在其他类中也需要连接数据库或者使用findCustomers()方法查询客户信息则难以实现代码的重用。无论是修改数据库连接方式还是修改图表显示方式都需要修改该类它不止一个引起它变化的原因违背了单一职责原则。因此需要对该类进行拆分使其满足单一职责原则。
2.改进 类CustomerDataChart拆分为如下三个类
DBUtil负责连接数据库包含数据库连接方法getConnection()CustomerDAO负责操作数据库中的Customer表包含对Customer表的增删改查等方法如findCustomers()CustomerDataChart负责图表的生成和显示包含方法createChart()和displayChart()
public class CustomerDataChartImprove {private CustomerDao customerDao;public void createChart() {System.out.println(创建图表...);}public void displayChart() {System.out.println(展示图表...);}
}public class CustomerDao {private DBUtil dbUtil;public void findCustomers() {System.out.println(查询客户信息...);}
}public class DBUtil {public void getConnetion() {System.out.println(数据库连接...);}
}二、开闭原则
开闭原则是面向对象的可复用设计的第一块基石它是最重要的面向对象设计原则。其定义如下开闭原则(Open-Closed Principle, OCP)一个软件实体应当对扩展开放对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展
为了满足开闭原则需要对系统进行抽象化设计抽象化是开闭原则的关键。在Java、C#等编程语言中可以为系统定义一个相对稳定的抽象层而将不同的实现行为移至具体的实现层中完成。在很多面向对象编程语言中都提供了接口、抽象类等机制可以通过它们定义系统的抽象层再通过具体类来进行扩展。如果需要修改系统的行为无须对抽象层进行任何改动只需要增加新的具体类来实现新的业务功能即可实现在不修改已有代码的基础上扩展系统的功能达到开闭原则的要求。
1.案例分析
某客户关系管理系统中可以显示各种类型的图表如饼状图和柱状图等为了支持多种图表显示方式设计方案如下 public class PieChart {public void display() {System.out.println(画饼状图表...);}
}
public class BarChart {public void display() {System.out.println(画柱状图表...);}
}
public class ChartDisplay {public void display(String type){if(pie.equals(type)){PieChart pieChart new PieChart();pieChart.display();}else if(bar.equals(type)){BarChart barChart new BarChart();barChart.display();}}
}
public class Client {public static void main(String[] args) {ChartDisplay chartDisplay new ChartDisplay();chartDisplay.display(pie);}
}问题在该代码中如果需要增加一个新的图表类如折线图LineChart则需要修改ChartDisplay类的display()方法的源代码增加新的判断逻辑违反了开闭原则。
2.改进
由于在ChartDisplay类的display()方法中针对每一个图表类编程因此增加新的图表类不得不修改源代码。可以通过抽象化的方式对系统进行重构使之增加新的图表类时无须修改源代码满足开闭原则。具体做法如下
增加一个抽象图表类AbstractChart将各种具体图表类作为其子类) ChartDisplay类针对抽象图表类进行编程由客户端来决定使用哪种具体图表 public abstract class AbstractChart {/*** 抽象方法*/public abstract void display();
}
public class BarChart extends AbstractChart {Overridepublic void display() {System.out.println(画柱状图表...);}
}
public class PieChart extends AbstractChart{Overridepublic void display() {System.out.println(画饼状图表...);}
}
public class ChartDisplay {private AbstractChart abstractChart;public void setAbstractChart(AbstractChart abstractChart) {this.abstractChart abstractChart;}public void display(){abstractChart.display();}
}
public class Client {public static void main(String[] args) {ChartDisplay chartDisplay new ChartDisplay();chartDisplay.setAbstractChart(new BarChart());chartDisplay.display();chartDisplay.setAbstractChart(new PieChart());chartDisplay.display();}
}三、里氏替换原则
如果对每一个类型为S的对象o1都有类型为T的对象o2使得以T定义的所有程序P在所有的对象o1都替换成o2时程序P的行为没有变化那么类型S是类型T的子类型。这个定义比较拗口且难以理解因此我们一般使用它的另一个通俗版定义 里氏替换原则(Liskov Substitution Principle, LSP)所有引用基类父类的地方必须能透明地使用其子类的对象。
在使用里氏代换原则时需要注意如下几个问题
子类的所有方法必须在父类中声明或子类必须实现父类中声明的所有方法。根据里氏代换原则为了保证系统的扩展性在程序中通常使用父类来进行定义如果一个方法只存在子类中在父类中不提供相应的声明则无法在以父类定义的对象中使用该方法。我们在运用里氏代换原则时尽量把父类设计为抽象类或者接口让子类继承父类或实现父接口并实现在父类中声明的方法运行时子类实例替换父类实例我们可以很方便地扩展系统的功能同时无须修改原有子类的代码增加新的功能可以通过增加一个新的子类来实现。里氏代换原则是开闭原则的具体实现手段之一。Java语言中在编译阶段Java编译器会检查一个程序是否符合里氏代换原则这是一个与实现无关的、纯语法意义上的检查但Java编译器的检查是有局限的。
1.案例分析
某客户关系管理系统中客户(Customer)可以分为VIP客户(VIPCustomer)和普通客(CommonCustomer)两类系统需要提供一个发送Email的功能设计方案如下 public class CommonCustomer {private String name;private String email;public String getName() {return name;}public void setName(String name) {this.name name;}public String getEmail() {return email;}public void setEmail(String email) {this.email email;}
}public class VIPCustomer {private String name;private String email;public String getName() {return name;}public void setName(String name) {this.name name;}public String getEmail() {return email;}public void setEmail(String email) {this.email email;}
}
public class EmailSender {public void send(CommonCustomer commonCustomer) {System.out.println(发送普通用户邮件...);}public void send(VIPCustomer vipCustomer) {System.out.println(发送Vip用户邮件...);}
}问题无论是普通客户还是VIP客户发送邮件的过程都是相同的也就是说两个send()方法中的代码重复而且在本系统中还将增加新类型的客户。为了让系统具有更好的扩展性同时减少代码重复使用里氏代换原则对其进行重构。
2.改进 public abstract class Customer {protected String name;protected String email;public String getName() {return name;}public void setName(String name) {this.name name;}public String getEmail() {return email;}public void setEmail(String email) {this.email email;}
}
public class EmailSender {public void send(Customer customer) {System.out.println(发送 customer.getName() 邮件...);}
}
public class Client {public static void main(String[] args) {Customer customer new CommonCustomer();customer.setName(普通用户);EmailSender sender new EmailSender();sender.send(customer);customer new VIPCustomer();customer.setName(VIP用户);sender.send(customer);}
}四、依赖倒转原则
如果说开闭原则是面向对象设计的目标的话那么依赖倒转原则就是面向对象设计的主要实现机制之一它是系统抽象化的具体实现。定义如下 依赖倒转原则(Dependency Inversion Principle, DIP)抽象不应该依赖于细节细节应当依赖于抽象。换言之要针对接口编程而不是针对实现编程。
依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中尽量引用层次高的抽象层类即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明以及数据类型的转换等而不要用具体类来做这些事情。为了确保该原则的应用一个具体类应当只实现接口或抽象类中声明过的方法而不要给出多余的方法否则将无法调用到在子类中增加的新方法。
在引入抽象层后系统将具有很好的灵活性在程序中尽量使用抽象层进行编程而将具体类写在配置文件中这样一来如果系统行为发生变化只需要对抽象层进行扩展并修改配置文件而无须修改原有系统的源代码在不修改的情况下来扩展系统的功能满足开闭原则的要求。
在实现依赖倒转原则时我们需要针对抽象层编程而将具体类的对象通过依赖注入(DependencyInjection, DI)的方式注入到其他对象中依赖注入是指当一个对象要与其他对象发生依赖关系时通过抽象来注入所依赖的对象。常用的注入方式有三种分别是构造注入设值注入Setter注入和接口注入。构造注入是指通过构造函数来传入具体类的对象设值注入是指通过Setter方法来传入具体类的对象而接口注入是指通过在接口中声明的业务方法来传入具体类的对象。这些方法在定义时使用的是抽象类型在运行时再传入具体类型的对象由子类对象来覆盖父类对象。
参照上面开闭原则例子
五、接口隔离原则
接口隔离原则(Interface Segregation Principle, ISP)使用多个专门的接口而不使用单一的总接口即客户端不应该依赖那些它不需要的接口。
根据接口隔离原则当一个接口太大时我们需要将它分割成一些更细小的接口使用该接口的客户端仅需知道与之相关的方法即可。每一个接口应该承担一种相对独立的角色不干不该干的事该干的事都要干。
1.案例分析
某客户关系管理系统中客户数据显示模块设计如下接口其中方法dataRead()用于从文件中读取数据方法createChart()用于创建图表方法displayChart()用于显示图表方法createReport()用于创建文字报表方法displayReport()用于显示文字报表。
public interface CustomerDataDisplay {void dataRead();void createChart();void displayChart();void createReport();void displayReport();
}
public class ConcreteClass implements CustomerDataDisplay{Overridepublic void dataRead() {System.out.println(数据读取...);}Overridepublic void createChart() {System.out.println(创建图表...);}Overridepublic void displayChart() {System.out.println(展示图表...);}Overridepublic void createReport() {System.out.println(创建报表...);}Overridepublic void displayReport() {System.out.println(展示报表...);}
}问题由于在接口CustomerDataDisplay中定义了太多方法即该接口承担了太多职责一方面导致该接口的实现类很庞大在不同的实现类中都不得不实现接口中定义的所有方法灵活性较差如果出现大量的空方法将导致系统中产生大量的无用代码影响代码质量另一方面由于客户端针对大接口编程将在一定程序上破坏程序的封装性客户端看到了不应该看到的方法没有为客户端定制接口。因此需要将该接口按照接口隔离原则和单一职责原则进行重构将其中的一些方法封装在不同的小接口中确保每一个接口使用起来都较为方便并都承担某一单一角色每个接口中只包含一个客户端如模块或类所需的方法即可。
2.改进 public interface DataHanler {void dataRead();
}
public interface ChartHandler {void createChart();void displayChart();
}
public interface ReportHandler {void createReport();void displayReport();
}
public class ConcreteClassImprove implements DataHanler, ChartHandler {Overridepublic void dataRead() {System.out.println(数据读取...);}Overridepublic void createChart() {System.out.println(创建图表...);}Overridepublic void displayChart() {System.out.println(展示图表...);}
}在使用接口隔离原则时我们需要注意控制接口的粒度接口不能太小如果太小会导致系统中接口泛滥不利于维护接口也不能太大太大的接口将违背接口隔离原则灵活性较差使用起来很不方便。
六、合成复用原则
合成复用原则又称为组合/聚合复用原则(Composition/Aggregate Reuse Principle, CARP)其定 义如下尽量使用对象组合而不是继承来达到复用的目的。
合成复用原则就是在一个新的对象里通过关联关系包括组合关系和聚合关系来使用一些已有的对象使之成为新对象的一部分新对象通过委派调用已有对象的方法达到复用功能的目的。简言之复用时要尽量使用组合/聚合关系关联关系少用继承。
1.案例分析
某客户关系管理系统中初期考虑到客户数量不多系统采用MySQL作为数据库与数据库操作有关的类如CustomerDAO类等都需要连接数据库连接数据库的方法getConnection()封装在DBUtil类中由于需要重用DBUtil类的getConnection()方法设计人员将CustomerDAO作为DBUtil类的子类设计方案如下
public class DBUtil {public void getConnection(){System.out.println(获取数据库连接...);}
}
public class CustomerDao extends DBUtil{public void addCustomer(){super.getConnection();System.out.println(添加用户信息...);}
}
问题随着客户数量的增加系统决定升级为Oracle数据库因此需要增加一个新的OracleDBUtil类来连接Oracle数据库由于在初始设计方案中CustomerDAO和DBUtil之间是继承关系因此在更换数据库连接方式时需要修改CustomerDAO类的源代码将CustomerDAO作为OracleDBUtil的子类这将违反开闭原则。
2.改进 public abstract class DBUtil {public abstract void getConnection();
}
public class OracleDBUtil extends DBUtil{Overridepublic void getConnection() {System.out.println(获取oracle数据库连接...);}
}
public class CustomerDao {private DBUtil dbUtil;public void addCustomer(){dbUtil.getConnection();System.out.println(添加用户信息...);}public void setDbUtil(DBUtil dbUtil) {this.dbUtil dbUtil;}
}CustomerDAO和DBUtil之间的关系由继承关系变为关联关系采用依赖注入的方式将DBUtil对象注入到CustomerDAO中可以使用构造注入也可以使用Setter注入。如果需要对DBUtil的功能进行扩展可以通过其子类来实现如通过子类OracleDBUtil来连接Oracle数据库。由于CustomerDAO针对DBUtil编程根据里氏代换原则DBUtil子类的对象可以覆盖DBUtil对象只需在CustomerDAO中注入子类对象即可使用子类所扩展的方法。
七、迪米特原则
迪米特法则(Law of Demeter, LoD)一个软件实体应当尽可能少地与其他实体发生相互作用。
如果一个系统符合迪米特法则那么当其中某一个模块发生修改时就会尽量少地影响其他模块扩展会相对容易这是对软件实体之间通信的限制迪米特法则要求限制软件实体之间通信的宽度和深度。迪米特法则可降低系统的耦合度使类与类之间保持松散的耦合关系。
迪米特法则还有几种定义形式包括不要和“陌生人”说话、只与你的直接朋友通信等在迪米特法则中对于一个对象其朋友包括以下几类
当前对象本身(this)以参数形式传入到当前对象方法中的对象当前对象的成员对象如果当前对象的成员对象是一个集合那么集合中的元素也都是朋友当前对象所创建的对象。
1.案例分析
某客户关系管理系统中包含很多业务操作窗口在这些窗口中某些界面控件之间存在复杂的交互关系一个控件事件的触发将导致多个其他界面控件产生响应例如当一个按钮(Button)被单击时对应的列表框(List)、组合框(ComboBox)、文本框(TextBox)、文本标签(Label)等都将发生改变在初始设计方案中界面控件之间的交互关系可简化为如下所示结构
由于界面控件之间的交互关系复杂导致在该窗口中增加新的界面控件时需要修改与之交互的其他控件的源代码系统扩展性较差也不便于增加和删除新控件。
可以通过引入一个专门用于控制界面控件交互的中间类(Mediator)来降低界面控件之间的耦合度。引入中间类之后界面控件之间不再发生直接引用而是将请求先转发给中间类再由中间类来完成对其他控件的调用。当需要增加或删除新的控件时只需修改中间类即可无须修改新增控件或已有控件的源代码
2.改进 总结
设计模式是一套经过反复使用的代码设计经验目的是为了重用代码、让代码更容易被他人理解、保证代码可靠性。 设计模式于己于人于系统都是多赢的它使得代码编写真正工程化它是软件工程的基石如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题每种模式在现实中都有相应的原理来与之对应每种模式描述了一个在我们周围不断重复发生的问题以及该问题的核心解决方案这也是它能被广泛应用的原因。 文章转载自: http://www.morning.fdfsh.cn.gov.cn.fdfsh.cn http://www.morning.bxnrx.cn.gov.cn.bxnrx.cn http://www.morning.smwlr.cn.gov.cn.smwlr.cn http://www.morning.yrmpz.cn.gov.cn.yrmpz.cn http://www.morning.bzcjx.cn.gov.cn.bzcjx.cn http://www.morning.smpmn.cn.gov.cn.smpmn.cn http://www.morning.rythy.cn.gov.cn.rythy.cn http://www.morning.gghhmi.cn.gov.cn.gghhmi.cn http://www.morning.synlt.cn.gov.cn.synlt.cn http://www.morning.bwznl.cn.gov.cn.bwznl.cn http://www.morning.lzsxp.cn.gov.cn.lzsxp.cn http://www.morning.xmjzn.cn.gov.cn.xmjzn.cn http://www.morning.tbzcl.cn.gov.cn.tbzcl.cn http://www.morning.rckdq.cn.gov.cn.rckdq.cn http://www.morning.gmrxh.cn.gov.cn.gmrxh.cn http://www.morning.zkrzb.cn.gov.cn.zkrzb.cn http://www.morning.ngjpt.cn.gov.cn.ngjpt.cn http://www.morning.wwdlg.cn.gov.cn.wwdlg.cn http://www.morning.wpcfm.cn.gov.cn.wpcfm.cn http://www.morning.mlcwl.cn.gov.cn.mlcwl.cn http://www.morning.sfyqs.cn.gov.cn.sfyqs.cn http://www.morning.lyhry.cn.gov.cn.lyhry.cn http://www.morning.nssjy.cn.gov.cn.nssjy.cn http://www.morning.knlbg.cn.gov.cn.knlbg.cn http://www.morning.hncrc.cn.gov.cn.hncrc.cn http://www.morning.lwcgh.cn.gov.cn.lwcgh.cn http://www.morning.syrzl.cn.gov.cn.syrzl.cn http://www.morning.dbbcq.cn.gov.cn.dbbcq.cn http://www.morning.xrrjb.cn.gov.cn.xrrjb.cn http://www.morning.cnprt.cn.gov.cn.cnprt.cn http://www.morning.czzpm.cn.gov.cn.czzpm.cn http://www.morning.ampingdu.com.gov.cn.ampingdu.com http://www.morning.mqldj.cn.gov.cn.mqldj.cn http://www.morning.kmprl.cn.gov.cn.kmprl.cn http://www.morning.mgskc.cn.gov.cn.mgskc.cn http://www.morning.youyouling.cn.gov.cn.youyouling.cn http://www.morning.pmjw.cn.gov.cn.pmjw.cn http://www.morning.bfysg.cn.gov.cn.bfysg.cn http://www.morning.nnykz.cn.gov.cn.nnykz.cn http://www.morning.yhxhq.cn.gov.cn.yhxhq.cn http://www.morning.hwnqg.cn.gov.cn.hwnqg.cn http://www.morning.mwmtk.cn.gov.cn.mwmtk.cn http://www.morning.rymd.cn.gov.cn.rymd.cn http://www.morning.pwksz.cn.gov.cn.pwksz.cn http://www.morning.lqws.cn.gov.cn.lqws.cn http://www.morning.sfdky.cn.gov.cn.sfdky.cn http://www.morning.gfjgq.cn.gov.cn.gfjgq.cn http://www.morning.hmsong.com.gov.cn.hmsong.com http://www.morning.dnqpq.cn.gov.cn.dnqpq.cn http://www.morning.rykx.cn.gov.cn.rykx.cn http://www.morning.ctlbf.cn.gov.cn.ctlbf.cn http://www.morning.wpsfc.cn.gov.cn.wpsfc.cn http://www.morning.pxlpt.cn.gov.cn.pxlpt.cn http://www.morning.fwnqq.cn.gov.cn.fwnqq.cn http://www.morning.sbncr.cn.gov.cn.sbncr.cn http://www.morning.mdtfh.cn.gov.cn.mdtfh.cn http://www.morning.rpms.cn.gov.cn.rpms.cn http://www.morning.bpmft.cn.gov.cn.bpmft.cn http://www.morning.ho-use.cn.gov.cn.ho-use.cn http://www.morning.htpjl.cn.gov.cn.htpjl.cn http://www.morning.qineryuyin.com.gov.cn.qineryuyin.com http://www.morning.nrfqd.cn.gov.cn.nrfqd.cn http://www.morning.wlfxn.cn.gov.cn.wlfxn.cn http://www.morning.xyhql.cn.gov.cn.xyhql.cn http://www.morning.bbmx.cn.gov.cn.bbmx.cn http://www.morning.mldrd.cn.gov.cn.mldrd.cn http://www.morning.ksggr.cn.gov.cn.ksggr.cn http://www.morning.pypbz.cn.gov.cn.pypbz.cn http://www.morning.lthtp.cn.gov.cn.lthtp.cn http://www.morning.fkgqn.cn.gov.cn.fkgqn.cn http://www.morning.bbjw.cn.gov.cn.bbjw.cn http://www.morning.tqsnd.cn.gov.cn.tqsnd.cn http://www.morning.xjbtb.cn.gov.cn.xjbtb.cn http://www.morning.mrbmc.cn.gov.cn.mrbmc.cn http://www.morning.hnrpk.cn.gov.cn.hnrpk.cn http://www.morning.jbshh.cn.gov.cn.jbshh.cn http://www.morning.rwxnn.cn.gov.cn.rwxnn.cn http://www.morning.xnlj.cn.gov.cn.xnlj.cn http://www.morning.wfyqn.cn.gov.cn.wfyqn.cn http://www.morning.hmbxd.cn.gov.cn.hmbxd.cn