12
返回列表 发新帖
楼主: tzhsuccess

J2SE综合

[复制链接]
论坛徽章:
66
ERP板块每日发贴之星
日期:2005-08-18 01:01:39生肖徽章2007版:兔
日期:2008-01-02 17:35:53生肖徽章2007版:牛
日期:2008-01-02 17:35:53生肖徽章2007版:蛇
日期:2008-04-07 19:42:14体育版块博采纪念徽章
日期:2008-07-03 19:47:13CTO参与奖
日期:2009-02-20 09:44:20生肖徽章2007版:狗
日期:2009-09-07 16:03:53ITPUB9周年纪念徽章
日期:2010-10-08 09:28:522013年新春福章
日期:2013-02-25 14:51:24生肖徽章:鸡
日期:2006-09-07 17:09:37
11#
 楼主| 发表于 2006-7-6 19:10 | 只看该作者
一些面向对象的设计法则(3)

法则3:开放-封闭法则(OCP)
  
  软件组成实体应该是可扩展的,但是不可修改的。
  
  [ Software Entities Should Be Open For Extension, Yet Closed For Modification
  ]
  
  
  
  
  
  
  
  
  开放-封闭法则
  
  
  
  
  
  
  1.开放-封闭法则认为我们应该试图去设计出永远也不需要改变的模块。
  
  2我们可以添加新代码来扩展系统的行为。我们不能对已有的代码进行修改。
  
  3.符合OCP的模块需满足两个标准:
  
  4.可扩展,即"对扩展是开放的"(Open For Extension)-模块的行为可以被扩展,以需要满足新的需求。
  
  5.不可更改,即"对更改是封闭的"(Closed for Modification)-模块的源代码是不允许进行改动的。
  
  6.我们能如何去做呢?
  
  a.抽象(Abstraction)
  
  b.多态(Polymorphism)
  
  c.继承(Inheritance)
  
  d.接口(Interface)
  
  
  
  7. 一个软件系统的所有模块不可能都满足OCP,但是我们应该努力最小化这些不满足OCP的模块数量。
  
  8.开放-封闭法则是OO设计的真正核心。
  
  9.符合该法则便意味着最高等级的复用性(reusability)和可维护性(maintainability)。
  
  OCP示例
  
  
  1. 考虑下面某类的方法:
  
   
  
  2.以上函数的工作是在制订的部件数组中计算各个部件价格的总和。
  
  3.若Part是一个基类或接口且使用了多态,则该类可很容易地来适应新类型的部件,而不必对其进行修改。
  
  4.其将符合OCP
  
  
  
  5. 但是在计算总价格时,若财务部颁布主板和内存应使用额外费用,则将如何去做。
  
  6.下列的代码是如何来做的呢?
  
   
  7.这符合OCP吗?
  
  8.当每次财务部提出新的计价策略,我们都不得不要修改totalPrice()方法!这并非"对更改是封闭的"。显然,策略的变更便意味着我们不得不要在一些地方修改代码的,因此我们该如何去做呢?
  
  9.为了使用我们第一个版本的totalPrice(),我们可以将计价策略合并到Part的getPrice()方法中。
  
  
  
  10.这里是Part和ConcretePart类的示例:
  
   
  
  11. 但是现在每当计价策略发生改变,我们就必须修改Part的每个子类!
  
  12.一个更好的思路是采用一个PricePolicy类,通过对其进行继承以提供不同的计价策略:
  
   
  
  
  13.看起来我们所做的就是将问题推迟到另一个类中。但是使用该解决方案,我们可通过改变Part对象,在运行期间动态地来设定计价的策略。
  
  14.另一个解决方案是使每个ConcretePart从数据库或属性文件中获取其当前的价格。
  
  
  
  
  
  
  
  
  
  
  单选法则
  
  
  
  
  
  
  单选法则(the Single Choice Principle)是OCP的一个推论。
  
  无论在什么时候,一个软件系统必须支持一组备选项,理想情况下,在系统中只能有一个类能够知道整个的备选项集合。
  
  
  
  

使用道具 举报

回复
论坛徽章:
66
ERP板块每日发贴之星
日期:2005-08-18 01:01:39生肖徽章2007版:兔
日期:2008-01-02 17:35:53生肖徽章2007版:牛
日期:2008-01-02 17:35:53生肖徽章2007版:蛇
日期:2008-04-07 19:42:14体育版块博采纪念徽章
日期:2008-07-03 19:47:13CTO参与奖
日期:2009-02-20 09:44:20生肖徽章2007版:狗
日期:2009-09-07 16:03:53ITPUB9周年纪念徽章
日期:2010-10-08 09:28:522013年新春福章
日期:2013-02-25 14:51:24生肖徽章:鸡
日期:2006-09-07 17:09:37
12#
 楼主| 发表于 2006-7-6 19:11 | 只看该作者
一些面向对象的设计法则(4)

法则4:Liskov替换法则(LSP)
  
  使用指向基类(超类)的引用的函数,必须能够在不知道具体派生类(子类)对象类型的情况下使用它们。
  
  [ Function Thar Use Referennces To Base(Super) Classes Must Be Able To Use Objects
  Of Derived(Sub) Classes Without Knowing It ]
  
  
  
  
  
  
  
  
  Liskov替换法则
  
  
  
  
  
  
  1.显而易见,Liskov替换法则(LSP)是根据我所熟知的"多态"而得出的。
  
  2.例如:
  
  
  
  方法drawShape应该可与Sharp超类的任何子类一起工作(或者,若Sharp为Java接口,则该方法可与任何实现了Sharp接口的类一起工作)
  
  但是当我们在实现子类时必须要谨慎对待,以确保我们不会无意中违背了LSP。
  
  
  
  
  
  
  
  
  3.若一个函数未能满足LSP,那么可能是因为它显式地引用了超类的一些或所有子类。这样的函数也违背了OCP,因为当我们创建一个新的子类时,会不得不进行代码的修改。
  
  
  
  
  
  
  
  
  
  LSP示例
  
  
  
  
  
  
  1. 考虑下面Rectangle类:
  
   
  
  2.现在,Square类会如何呢?显然,一个正方形是一个四边形,因此Square类应该从Rectangle类派生而来,对否?让我们看一看!
  
  3.观察可得:
  
  a.正方形不需要将高和宽都作为属性,但是总之它将继承自Rectangle。因此,每一个Square对象会浪费一点内存,但这并不是一个主要问题。
  
  b.继承而来的setWidth()和setHeight()方法对于Square而言并非真正地适合,因为一个正方形的高和宽是相同。因此我们将需要重写setWidth()和setHeight()方法。不得不重写这些简单的方法有可能是一种不恰当的继承使用方式。
  
  
  
  3.Square类如下:
  
   
  
  4. 看起来都还不错。但是让我们检验一下!
  
   
  
   
  
  5. 测试程序输出:
  
  
  
  6.看上去好像我们违背了LSP!
  
  
  
  7.这里的问题出在哪里呢?编写testLsp()方法的程序员做了一个合理的假设,即改变Rectangle的宽而保持它的高不变。
  
  8.在将一个Square对象传递给这样一个方法时产生了问题,显然是违背了LSP
  
  9.Square和Rectangle类是相互一致和合法的。尽管程序员对基类作了合理的假设,但其所编写的方法仍然会导致设计模型的失败。
  
  10.不能孤立地去看待解决方案,必须根据设计用户所做的合理假设来看待它们。
  
  
  
  11. 一个数学意义上的正方形可能是一个四边形,但是一个Square对象不是一个Rectangle对象,因为一个Square对象的行为与一个Rectangle对象的行为是不一致的!
  
  12.从行为上来说,一个Square不是一个Rectangle!一个Square对象与一个Rectangle对象之间不具有多态的特征。
  
  
  
  
  
  
  
  
  
  
  
  
  
  总结
  
  
  
  
  
  
  1.Liskov替换法则(LSP)清楚地表明了ISA关系全部都是与行为有关的。
  
  2.为了保持LSP(并与开放-封闭法则一起),所有子类必须符合使用基类的client所期望的行为。
  
  3.一个子类型不得具有比基类型(base type)更多的限制,可能这对于基类型来说是合法的,但是可能会因为违背子类型的其中一个额外限制,从而违背了LSP!
  
  4.LSP保证一个子类总是能够被用在其基类可以出现的地方!
  
  
  
  







  
   


--------------------------------------------------------------------------------

使用道具 举报

回复
论坛徽章:
66
ERP板块每日发贴之星
日期:2005-08-18 01:01:39生肖徽章2007版:兔
日期:2008-01-02 17:35:53生肖徽章2007版:牛
日期:2008-01-02 17:35:53生肖徽章2007版:蛇
日期:2008-04-07 19:42:14体育版块博采纪念徽章
日期:2008-07-03 19:47:13CTO参与奖
日期:2009-02-20 09:44:20生肖徽章2007版:狗
日期:2009-09-07 16:03:53ITPUB9周年纪念徽章
日期:2010-10-08 09:28:522013年新春福章
日期:2013-02-25 14:51:24生肖徽章:鸡
日期:2006-09-07 17:09:37
13#
 楼主| 发表于 2006-7-6 19:12 | 只看该作者
OOP Java:beans中的一个小小的事物逻辑

传统上,任何提供商务基本功能的软件都涉及到了事物逻辑。除了定义为实现标准服务或者应用程序的事物规则的代码外,你还可以进一步定义事物逻辑的概念。事物逻辑一般与n层系统的中间层相联系。换句话说,事物逻辑在表示层与数据访问层之间,如图A所示。
  图A
   
  事物逻辑组建或者服务一般用来响应表示层的请求、访问数据层中特定资源以获取查询结果,并把查询结果返回到表示层。事物逻辑组件应该对任何用户都是一碗水端平的。这就允许它们用一种与客户无关的方式提供功能。下面,我们将看到JavaBean是如何帮助你实现这一点。不过,在这之前,让我们首先看看什么是软件组件(software component)以及它与对象(object)的区别。
  有关组件的一个案例
   
  
  软件组件可以定义为一种自治的、提供外部公共接口、动态可用的(dynamically discoverable)事物过程,软件组件还可以用来构件其它组件或者应用程序。打个比方,汽车的每个零件(组件)都有明确的目的(过程),只要零件的尺寸和螺孔(接口)符合标准,它就可以用来组装一辆完整的汽车(其它组件或者应用程序)。
  
  也许举个例子可以帮助你理解这一点。假设有一家低水平的软件公司,它有如下的软件技术:电子表格、文本编辑、图形编辑工具、拼写检测。现在,如果说这些技术中的每一种都有一个对应的公共接口可供其它软件组件或者应用程序来使用。发挥一下你的想象力,你就会发现把这些技术组合在一起形成一个功能强大的应用程序――例如字处理软件――并不会花费多少力气。
  
  你也许会说:“软件组件与对象或者代码库有什么区别?”――这是个不错的提问。让我们看看它们之间的区别。
  库、组件和对象
   
  
  单一的代码库是函数调用的一个逻辑上的组合,它们按照与操作系统有关的机器代码的方式打包到一个可链接的文件上。在Java中,库可能是以打包到一个JAR文件一组静态方法的形式存在的。由于这些函数调用或者静态方法对它们所处的环境一无所知,所以它们一般不能维护(与调用它们的客户代码有关的)任何一种状态。例如,Java中的代码库(打包为一组静态方法),看上去可能向代码清单A所示。
  清单A如下:
  Listing A
  
  
  public class SystemLib {  public static Date getDateTime()  {   return new Date();  }  public static String getOSVersion()  {   return System.getProperty("os.version";  } }
  
  对象把状态和功能封装当逻辑单元之中。一个或者多个客户可以分享同一个对象。对象一般可以提供其中某些状态数据以及适当的操作。代码清单B给出了一个表示个人信息的Java对象。
  代码清单B如下:
  Listing B
  
  
  public class Person {  protected String name;  protected int age;  public Person(String name, int age)  {   this.name = name;   this.age = age;  }  public String getName()  {   return name;  }  public int getAge()  {   return age;  }  public String toString()  {   return "Name: " + name + ", Age: " + age;  } }
  
  
  请注意观察Person对象是如何提供状态数据(姓名与年龄)以及对应的一个操作(toString)。这些都是相当基本的。那么软件组件又有什么好抄作的呢?
  
  软件组件把对象的概念向前推进了若干步。软件组件像对象那样把状态和操作封装在同一个逻辑单元之中,但与对象不同,软件组件还提供了动态可用的接口、属性和操作的概念。典型的组件还提供了对象的事件概念。让我们看看如何把Person对象扩展、提升成一个组件,代码如清单C所示。
  清单C如下:
  Listing C
  
  
  public class Administrator extends Person {  private String rights; 
  private String department; 
  private Vector listeners = new Vector(); 
  public Administrator(String name, int age, String rights, String department)  {
  super(name, age); 
  this.rights = rights; 
  this.department = department;  } 
  public String getRights()  {   return rights;  }  public void setRights(String rights)  { 
  this.rights = rights;   
  fireStateChangedEvent("Rights";  } 
  public String getDepartment()  {   return department;  } 
  public void setDepartment(String department)  {   this.department = department;   fireStateChangedEvent("Department";  }  public void setName(String name)  {   this.name = name;   fireStateChangedEvent("Name";  }  public void setAge(int age)  {   this.age = age;   fireStateChangedEvent("Age";  }  public void addStateChangedListener(StateChangedListener l)  {   if (listeners.indexOf(l) == -1)   {     listeners.add(l);   }  }  public void removeStateChangedListener(StateChangedListener l)  {   listeners.remove(l);  }  private void fireStateChangedEvent(String dataName)  {   for (Iterator iter = listeners.iterator(); iter.hasNext()   {     StateChangedListener l = (StateChangedListener)iter.next();     l.stateChanged(new StateChangedEvent(this, dataName));   }  }  public String toString()  {   return super.toString()     + ", Rights: " + rights + ", Department: " + department;  } }
  
  Administrator类扩展了Person类,它添加了两个新的数据(或者说属性):权利(rights)和部门(department)。Administrator类提供了修改权利和部门属性的操作。它还添加了改变父类(Person类)的姓名和年龄的操作。Administrator类添加了事件功能,因此有兴趣的客户可以登记为Administrator类事件的接受者,这样该用户就可以知晓Administrator组件中的任何状态改变。
  
  Java中的Bean
  Java编程语言中正式的组件模型是JavaBeans规范。该规范增加了若干个概念以提升Java编程语言中组件的性能。其中的某些概念――属性、事件以及方法――与其它标准组件模型中的对应概念含义相同。尽管方法的概念对JavaBeans来说并不新鲜,但是JavaBeans模型发布关于方法的信息还是可以使得在设计和运行时发现它们更容易。
  
  JavaBeans规范引入的最最广为使用的一个概念(尽管从表面上看这个概念相当的简单)就是命名方式。命名方式就是用推荐的前缀和后缀来区分方法的简单标准。例如,JavaBeans规范建议用来任何提供属性值(状态数据)的方法的名字应该有前缀get或者is。这样,提供年龄(age)属性值的方法应该命名为getAge()。另外,任何修改属性值的方法的名字都应该以set为前缀。这样,修改年龄属性的方法应该命名为setAge()。代码清单C中的Person类就演示了如何使用这种命名方式。
  
  JavaBeans规范中另一个广为使用的概念就是任何JavaBeans都要支持串行化(serialization)和具体化(externalization)的要求。这两个要求都用来处理对象自动写入存储器以及从存储器恢复的能力。串行化一般由java.io包来控制,而具体化的工作大多由单独的类来控制。
  
  命名方式、串行化、具体化的概念简洁明了,它们为Java编程语言以及Web应用程序开发环境提供了巨大的动力。让我们看看几种在事物逻辑层利用这些JavaBeans概念的方法。
  Beans的事物逻辑层
   
  
  由于JavaBeans必须支持串行化和通用命名方式,所以它们对数据传输组件自适应。例如,假设我们在利用银行系统的组件和服务,那么我们的事物逻辑层就应该考虑诸如帐号、客户和付款之类的组件。这些组件需要在组件与组件之间、层与层之间传递。这个传递机制可能需要跨越处理边界以及机器边界。为了使得组件可以方便的跨越这些边界,该组件就需要在电缆传输时拥有串行化和解串行化(解串)的能力。由于JavaBeans需要支持串行化,又由于我们可以依靠JavaBeans的通用命名方式,它们可以在过程之间或者层之间动态的串行传输以及解串(deserialized),如图B所示。
  图B
  
  总结
  我们已经介绍了如何用Java以及JavaBeans来建造事物逻辑组件以及中间件的技术。我们将在下一篇文章中介绍Enterprise JavaBeans在与其它资源交互时是如何用来封装事物逻辑的,并由此进一步展开对事物逻辑组件的讨论。
  

使用道具 举报

回复
论坛徽章:
66
ERP板块每日发贴之星
日期:2005-08-18 01:01:39生肖徽章2007版:兔
日期:2008-01-02 17:35:53生肖徽章2007版:牛
日期:2008-01-02 17:35:53生肖徽章2007版:蛇
日期:2008-04-07 19:42:14体育版块博采纪念徽章
日期:2008-07-03 19:47:13CTO参与奖
日期:2009-02-20 09:44:20生肖徽章2007版:狗
日期:2009-09-07 16:03:53ITPUB9周年纪念徽章
日期:2010-10-08 09:28:522013年新春福章
日期:2013-02-25 14:51:24生肖徽章:鸡
日期:2006-09-07 17:09:37
14#
 楼主| 发表于 2006-7-6 19:13 | 只看该作者
Java面向对象编程实例详解

Java是一种面向对象的语言,是实现面向对象编程的强大工具。但如何在编程中实际运用并发挥其最大效能呢?本文通过一个实际Java程序的开发过程,详细说明了如何使用面向对象实现Java编程。
   
  
  
  我们要实现的Java应用程序是:当用户输入一个球体的半径,程序将显示该球体的体积与表面积。在您阅读下文以前,请您自己思考一分钟,您将如何设计该Java应用程序。
  
  
  
  一、普通实现方法
  我相信大多数程序员要实现上述功能的程序,非常迅速地、自信地将采用下面的实现代码:
  
  class Sphere
  
  {
  
  public static void main(String[] args)
  
  {
  
  EasyReader console = new EasyReader();
  
  System.out.print("Enter the radius: ";
  
  double radius = console.readDouble();
  
  System.out.println("Radius = " + radius);
  
  double volume = 4.0 / 3.0 * Math.PI * radius * radius * radius;
  
  System.out.println("Volume = " + volume);
  
  double surfArea = 4.0 * Math.PI * radius * radius;
  
  System.out.println("Surface area = " + surfArea);
  
  }
  
  }
  
  EasyReader类代码如下:
  
  import java.io.*;
  
  public class EasyReader
  
  {
  
  protected String myFileName;
  
  protected BufferedReader myInFile;
  
  protected int myErrorFlags = 0;
  
  protected static final int OPENERROR = 0x0001;
  
  protected static final int CLOSEERROR = 0x0002;
  
  protected static final int READERROR = 0x0004;
  
  protected static final int EOF = 0x0100;
  
  
  
  /**
  
  * Constructor. Prepares console (System.in) for reading
  
  */
  
  public EasyReader()
  
  {
  
  myFileName = null;
  
  myErrorFlags = 0;
  
  myInFile = new BufferedReader(
  
  new InputStreamReader(System.in), 128);
  
  }
  
  
  
  /**
  
  * Constructor. opens a file for reading
  
  * @param fileName the name or pathname of the file
  
  */
  
  public EasyReader(String fileName)
  
  {
  
  myFileName = fileName;
  
  myErrorFlags = 0;
  
  try
  
  {
  
  myInFile = new BufferedReader(new FileReader(fileName), 1024);
  
  }
  
  catch (FileNotFoundException e)
  
  {
  
  myErrorFlags |= OPENERROR;
  
  myFileName = null;
  
  }
  
  }
  
  
  
  /**
  
  * Closes the file
  
  */
  
  public void close()
  
  {
  
  if (myFileName == null)
  
  return;
  
  try
  
  {
  
  myInFile.close();
  
  }
  
  catch (IOException e)
  
  {
  
  System.err.println("Error closing " + myFileName + "n";
  
  myErrorFlags |= CLOSEERROR;
  
  }
  
  }
  
  
  
  /**
  
  * Checks the status of the file
  
  * @return true if en error occurred opening or reading the file,
  
  * false otherwise
  
  */
  
  public boolean bad()
  
  {
  
  return myErrorFlags != 0;
  
  }
  
  
  
  /**
  
  * Checks the EOF status of the file
  
  * @return true if EOF was encountered in the previous read
  
  * operation, false otherwise
  
  */
  
  public boolean eof()
  
  {
  
  return (myErrorFlags & EOF) != 0;
  
  }
  
  
  
  private boolean ready() throws IOException
  
  {
  
  return myFileName == null || myInFile.ready();
  
  }
  
  
  
  /**
  
  * Reads the next character from a file (any character including
  
  * a space or a newline character).
  
  * @return character read or null character
  
  * (Unicode 0) if trying to read beyond the EOF
  
  */
  
  public char readChar()
  
  {
  
  char ch = 'u0000';
  
  
  
  try
  
  {
  
  if (ready())
  
  {
  
  ch = (char)myInFile.read();
  
  }
  
  }
  
  catch (IOException e)
  
  {
  
  if (myFileName != null)
  
  System.err.println("Error reading " + myFileName + "n";
  
  myErrorFlags |= READERROR;
  
  }
  
  
  
  if (ch == 'u0000')
  
  myErrorFlags |= EOF;
  
  
  
  return ch;
  
  }
  
  
  
  /**
  
  * Reads from the current position in the file up to and including
  
  * the next newline character. The newline character is thrown away
  
  * @return the read string (excluding the newline character) or
  
  * null if trying to read beyond the EOF
  
  */
  
  public String readLine()
  
  {
  
  String s = null;
  
  
  
  try
  
  {
  
  s = myInFile.readLine();
  
  }
  
  catch (IOException e)
  
  {
  
  if (myFileName != null)
  
  System.err.println("Error reading " + myFileName + "n";
  
  myErrorFlags |= READERROR;
  
  }
  
  
  
  if (s == null)
  
  myErrorFlags |= EOF;
  
  return s;
  
  }
  
  
  
  /**
  
  * Skips whitespace and reads the next word (a string of consecutive
  
  * non-whitespace characters (up to but excluding the next space,
  
  * newline, etc.)
  
  * @return the read string or null if trying to read beyond the EOF
  
  */
  
  public String readWord()
  
  {
  
  StringBuffer buffer = new StringBuffer(128);
  
  char ch = ' ';
  
  int count = 0;
  
  String s = null;
  
  
  
  try
  
  {
  
  while (ready() && Character.isWhitespace(ch))
  
  ch = (char)myInFile.read();
  
  while (ready() && !Character.isWhitespace(ch))
  
  {
  
  count++;
  
  buffer.append(ch);
  
  myInFile.mark(1);
  
  ch = (char)myInFile.read();
  
  };
  
  
  
  if (count > 0)
  
  {
  
  myInFile.reset();
  
  s = buffer.toString();
  
  }
  
  else
  
  {
  
  myErrorFlags |= EOF;
  
  }
  
  }
  
  
  
  catch (IOException e)
  
  {
  
  if (myFileName != null)
  
  System.err.println("Error reading " + myFileName + "n";
  
  myErrorFlags |= READERROR;
  
  }
  
  
  
  return s;
  
  }
  
  
  
  /**
  
  * Reads the next integer (without validating its format)
  
  * @return the integer read or 0 if trying to read beyond the EOF
  
  */
  
  public int readInt()
  
  {
  
  String s = readWord();
  
  if (s != null)
  
  return Integer.parseInt(s);
  
  else
  
  return 0;
  
  }
  
  
  
  /**
  
  * Reads the next double (without validating its format)
  
  * @return the number read or 0 if trying to read beyond the EOF
  
  */
  
  public double readDouble()
  
  {
  
  String

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表