公孙龙,六国时辩士也。疾名实之散乱,因资材之所长,为“守白”之论。假物取譬,以“守白”辩,谓白马为非马也。
以马作为进行问题域进行建模,已知存在白马这种类型。显然存在马的超类,并且马类包含一个属性-颜色,是否需要建立白马的子类呢?显而易见的是,当马的颜色属性是白色时,马的一些实例表达了一个白马的特殊实例群(由此我们可以得知:白马显然是马),根据里氏替换原则,子类型必须能够替换掉它们的基类型,显然在分析了马的行为模式以后,我们可以得出结论:白马可以替换马。----!难道真的要建立白马、黑马、x马的子类吗?
我认为可以从以下几方面进行分析。
1、类的职责(很大程度上等同于服务能力,操作方式):
设计一个类,首先要从类职责的分析入手,一个类要承担响应的职责,反过来说同样的职责应该由同样的类承担,否则会造成类泛滥,实例孤单的状况。如果领域内马和白马承担同样的职责,应该只建立马一个类,不应该只见树不见林,造成不抽象的类的产生。
2、类的行为模式(当类承担响应职责时,如何扩展):
分析一个类要从类的行为模式入手,既然一个类要承担责任,其承担的责任表现方式是否一样呢呢?这就是她的行为模式,这也是里氏替换原则主要起作用的地方,如果两个类的职责相当,但行为模式不同则不能成为超类和子类的关系(比如"著名"的正方形不是长方形问题)。马类,作为超类,基于一些特殊的行为方式:吃草,跑...,对于白马她的行为模式和马是一样的,并没有不同,所以白马是马而非马的同根继承子类。对于长方形和正方形都是具有相同计算面积算法(职责)的四方形的子类。
-------以下是关于此事的一些扩展分析-----------
3、子类的产生:
何时需要产生子类呢:是对其父类的职责进行扩展,白马没有对父类的职责进行扩展,所以不是马的子类。首先子类要扩展超类,其次子类不能重写或废除超类的职责。
4、属性,状态的区别(类的域)
对于一些类,在状态不同时,会有不同的表现(状态机模式),所以,类的getter,setter的部分包含两种不同的特性,对于属于状态的部分,是我们要仔细分析的,而"白"马则属于属性类(非状态)的域, 一般来讲,一个类的实例要能提供相应的差异服务(由于状态不同)最好使用不变模式[生存周期状态不变]或状态机[生存周期有状态,但状态不由调用者控制]来实现。
5、抽象类和接口
由于java的单根继承特性,很多设计人员不敢定义抽象类为继承树根,一定要先定义马的接口,在建立抽象马,作为一种"准规范"无可厚非,但我认为这是不愿承担责任的表现,有行为的基类应该可以(必须?)从类定义开始,避免白马类(一旦马成为接口,白马的产生就更加"名正言顺"了)的出现.将来如果发生变化可以通过重构(导出接口和使用委托),解决问题。
6、对象的创建(组装)和使用应该分开
既然对象的状态如此重要,属性有有很大程度的不变性(白马在构造时就用该是白的,并且一生不变),而骑马的人不必要求马的属性(!),所以,我们应该将马的构造和使用分开,使领域模型更清晰。使用一些ioc容器,比如spring就能很好的解决这些问题。
7、分析问题的领域
说了这么多,有一个问题;如果有一个马的研究机构,专门对不同颜色的马进行专题研究,马的颜色可能会对马的行为有很大影响,例如战马如果是黄色(绿色,哈哈)更利于伪装,此时"白"可能是一个很关键的问题,颜色会影响到不同的伪装策略,此时将白马作为马的一个子类则是必须的!所以问题域不同,类的设计就不同,生活中的问题域比较清晰(生物学家和厨师对马的理解不同),而软件建模时往往问题域混杂,这也是oo设计时比较困难的问题,所以分析问题域也是非常重要的设计问题。
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器