父类对象跟子类对象之间究竟能否equals

父类对象和子类对象之间究竟能否equals
http://www.iteye.com/topic/1119409?page=4

   楼主的问题,让我现在思维都还不能停止。现整理了一番,同时也是对自已一次认知的梳理。

   楼主的问题是:父类对象和子类对象之间究竟能否equals。并举了一个例子:
引用

   一个Employee员工类,一个Manager类,是员工类的子类。

   经理肯定是员工,员工未必一定是经理。 如存在某经理,那么他可能有一个Employee类对象,也可能有一个Manager类对象,如果这两个对象调用equals方法,是否应返回true

   假定上述答案是肯定的,那么我们在重写equals方法的时候就不可以再用instance of判定了。因为会违反自反性。(A equals B 则必有B equals A)。

   从面向对象的设计的角度说,这个问题应该怎么看?

   是不同类的对象之间equals永远返回false,还是不应该在equals方法中先用instance of来做下判定?


   首先从JAVA语言来说,equals是允许程序员自行修改的。

   从软件设计角度来说,又怎么样呢?不同的人对软件设计的理解、方法掌握不同,答案又是不一样的。

   如果从我的软件设件理解来说 ,不同类的对象之间的equals是永远为false.

   如果在我负责的项目中,一经发现两个不同类的对象之间需要修改equals时,我会在我的需求设计阶段之需求建模时进行检查,我的需求模型倒底是哪里出现严重的错误

   因为在我的软件设计世界里,软件是对现实世界部份的模拟。
  
   这种理念的优点是:当在软件设计时,遇到困难,可以从要模仿的现实世界去观察、去分析、去建模,从而解决软件设计中的困难。

    在这个理念下,我的程序下,一个实例,就像现实世界中一个鲜活的生命,独一无二的,他一经死亡(即实例注销)就无法复活。 但是我是允许他冬眠的,即让一个实例保存到文件中或数据库里,他一旦从冬眠中结束,恢复到程序世界里,这个过程像冬眠的动物从睡眠中回到真实世界里。愎复到程序世界的过程,在程序表现就是一次从文件中加载并实例化的过程,由于动物从冬眠恢复后,他的生命还是原来的生命,他还是独一无二的,因此从数据中恢复一个实例,该实例还是必须独一无二的,不允许多次加载形成不同的实例。
 
    所以在我的软件设计里,一旦发现需要修改equals时,我会从需求建模的模型中去查找我的严重、致命错误。

   拿楼主举例来说,当一个人是经理时,他肯定是员工。在楼主眼中,这个人,有两个化身--------经理实例与---------员工实例, 从而需要修改equals。但是在我的眼中,一个人,永远是一个人,是一个独一无二,不可复制的人,这个人不会因为身份的变化,他就变成两个人,不会因为是经理他是一个人,当成董事长时,他又是另一个人。

   那么建模时,我应该如何解决"一个人的多重身份问题"呢?首先从功能上要区分开几个概念,人 、经理、员工。经理与员工是身份,他不是生命,人才是真正的生命。身份是人的一个属性,可以允许有多种,即经理 、员工及其他。如果这几个概念理清了,那么我的模型就是:
  
   public class person{//代表一个生命
      private List<Standing> standings;//身份
   }
   public class Standing{//身份}
   public class Manager extends Standing {//经理} 
   public class Employee extends Standing {//员工}
   

   如果在项目中,身份最低就是员工,最多是经理,没有其他时,可以化减模型
  
   public class Employee{//员工; 
      private  Manager manager;//身份,不为null表示经理,否则是普通员工
   }
   public class Manager{}



   如果有人要问,在程序设计中,一定会出现类继承现象,假定某个模型就是

 
public class Manager extends Employee {//经理继承员工}


   如果由于需要,需要把Manager 与 Employee 保存到数据库中,并恢复过来。由于员工、经理的不同,他们的的都需要加载。

   即 会出现某个出书大师-----redhat 提示要出现的调用问题:
  
引用

   m=managerDao.loadFromDb(1);
   e=employeeDao.loadFromDb(1);


    什么意思呢?就是不同的类(Manager 与EmployeeDao 即经理与员工类),在加载同一个员工编号时,所得到的两个类实例,不是不一样吗?不是需要判断这两个是等同的吗?

    在我的软件世界中,首先,不管是用父类加载,还是用子类加载,在程序中只装加载一次。也就是说,一个生命从冬眠中醒过来,不允许变成两个生命。

    由于方便 其他地方编程的需要,我也同时允许父类与子类加载数据形成实例。但是,对于一个员工,如果他是一个经理,在通过员工类加载数据时,绝对不允许让他不能变成经理,或者说不能让他丢失经理身份。

    那么在模型中,如何设计呢?

    具体怎么设计呢?
   
    public class Employee{//员工; 
        private  Manager manager;//身份,不为null表示经理,否则是普通员工
        public Employee load(xxxx){//从数库中加载,并形成一个实例。
           //算法描述为: 先在经理表中,查询该员工是否是经理,如果是经//理,按经理类的加载方法加载,并返回一个Manager对象;如果
//不是经理,按普通员式方法加载,并返回一个Employee对象。
        }
     }

    public class Manager extends Employee{//经理
         public Manager load(xxxx){//从数库中加载,并形成一个实例
     }


     所以,在我的软件设计世界中,不同类的对象之间equals永远是false.

     我把我的理解整理出能,希望能给某些朋友以启迪

     最后,我已经把上面这一段,存放到我的blog里,望楼主不要介怀。


    //==========================本想结束,有朋友继续问,只好继续一下=========

    
kidneyball 写道
楼上的朋友,你的设计在父类里硬编码了子类,没法扩展呀。如果我又想从Employee里继承出来一个TempEmployee(临时工),那不是又要往Employee里加一个TempEmployee的对象域?

另外,“如果在项目中,身份最低就是员工,最多是经理,没有其他时,可以化减模型”这句话谁也不敢写包单的,还是遵循开闭原则比较稳妥。


  朋友说得很到位。 
 
   关于硬编码 ,就要看怎么编码了,编得好就能扩展,编得不好,就扩展不了。 本来第一感觉是用策略模式,把不同的加载方法进行封装。但时,在使用中是非常不方便的,当需要用父类加载所有子类的实例时,通常不知道子类的类型,所以取消策略模式的念头。

  那么上面的方法就真的不能扩展子类了吗? 答案是否定的。关键在于,保存上的数据格式设计问题。 在数据库中,有一表专门存储各种子类的检索表,当某个子类保存到数据库时,他保存的地方,就必须要在检索表中注册,并严格按照检索表的约定去存放。 这样父类加载时,先查遍检表的信息,以确定子类身份,并按相应方式加载。

  这种方法效率有点低,特别是当子类众多时。  但是,不管怎么说,至少有一种方法能实现子类扩展问题。再有的继承上,去优化,去思考效率更高的方法。

  另外,当这种情况----即子类特别众多,我会怎么办?

  在我的软件设计世界中,我又会马上去检查我的需求模型设计,这里又出了致命的重大问题。
 
  因为,在我的软件设计世界中,这种类继承关系,不是用来表示软件的被操作部份-----即数据部份;而是用来表示操作者--即程序本身。 道理非常的简单,现在的大多数面向对象的编辑语言,一旦类写好,类继承关系确定好,在程序运行里,是非常难改变继承关系的,也很难改把运行实例的类,改成另一个全新运行的类。 如果用类继承关系去描述那些被操纵的数据部份,这些对象的继承关系一旦发生变化,整个项目再难继续进行。

  那么,当程序要操作的 对象的类,本身有众多的继承关系怎么办?

  在我的软件设计世界中,我会把这些关系,用数据结构去描绘,去建模,而不会用类继承关系建模。类继承关系建模,永远给软件的控制系统部份。请对数据结构重视不够,或理解不够深的朋友,请不要轻易让其他同行放弃数据结构的学习与领悟 当然了,这是从我的理解角度 说出的建议了。
 
  故当有众多子类需要保存到数据库中,我会检查我的需求建模。

   对于第二个问题
引用
另外,“如果在项目中,身份最低就是员工,最多是经理,没有其他时,可以化减模型”这句话谁也不敢写包单的,还是遵循开闭原则比较稳妥。


   首先你的这个提法,不错,我赞同。
   但是,如果项目价值不大,花的成本很小,需求变化特小,按适合就是最好的原则,按简化的做,没问题。