设计模式问题

时间:2009-12-17 17:44:31

标签: design-patterns

我是设计模式的新手,这是我的问题

如果我们有一个抽象类,几乎没有实现它的类,并且每个类都有不同的属性。

现在我有另一个(Manager类),它包含一个抽象类的数组,我想在其中放入一个搜索方法...如何在不转换为具体类的情况下这样做?

我有两个想法:

第一个:添加额外级别的接口(即,而不是转换为具体类,我将转换为接口),这与代码接口而不是实现规则... 但是当我添加另一个类时,我将不得不为它创建一个接口,我还必须编辑管理器(客户端),这似乎不太好。

第二个解决方案: 它看起来有点奇怪,仍然需要增强,但它的主要目标是让管理器或任何其他客户端使用抽象类,而不知道谁扩展它或它的属性。

解决方案如下: 添加的每个新项目都必须覆盖一个界面,该界面强制执行它以生成其字段的完整描述,例如,汽车对象必须返回 具有以下

的哈希映射

字段:{fieldType,fieldValue}

示例

  • model:{text,“ford”}
  • manifactureDate:{Date,“12/1/89”}

并且每个对象也将实现一个名为compareFields的方法,该方法采用a 像这样的哈希映射并将其与其字段进行比较并返回true或false。

现在通过这种方式我解决了很多问题 - 对于gui,我只需要为这个可以显示的hashmap创建一个渲染引擎 任何项目,而不必知道它的类型。 (再次,gui是抽象类的另一个客户端)

- 对于搜索,我可以获得包含用户输入字段的哈希映射 在搜索表单中循环抽象项并调用compare fieldmethod

我仍然不知道我将如何处理复杂对象(有另一个对象作为其属性)

我不知道这是什么样的模式..这只是我想到的一个想法。

编辑:具体示例

如果我有一个带有汽车和公共汽车的抽象项目类和实现它的船,并且每个类都有不同的属性.... 如何管理员例如交通管理员使用抽象类搜索某个项目而不会投射到汽车或公共汽车...... 真的很抱歉这个长期的问题

8 个答案:

答案 0 :(得分:9)

<强>封装

封装的OO原则指出,您反对不应将其状态暴露给外部。如果你的对象在内部信息上显示它打破了封装。根据OO设计,仍然可以将搜索标准传递给对象,并让它决定它们是否匹配。

interface IVehicle{
   bool doesMatch( Map<String,String> searchCriterion )
}

您可以在所有车辆上安装一个交互器,并检索那些与打破封装相匹配的车辆。车辆的特定实施仍可根据需要重新实施。

<强>访问者

否则,我建议您查看Visitor模式。然后,想法是遍历所有对象,并有一个额外的类处理每种特定类型的处理。这也打破了纯封装(因为对象需要将其数据暴露给访问者),但它非常优雅。

class VehicleSearchVisitor
{
   Map<String,String> searchCriterion;
   void visit( Car car ) {...}
   void visit( Bike bike ) { ... }
   ....
}

<强>元编程

自描述对象的概念是另一个概念,称为元编程。表示层然后内省其他对象以了解如何处理它们。传统上这被认为是一种先进的OO技术。您可以创建自定义注释来描述类的字段,以便表示层可以动态呈现相应的标签。例如,与hibernate注释一起使用相同的想法。元编程需要仔细完成,否则你会遇到其他问题。

<强> Insteanceof

使用insteanceof也是一种内省形式(因为你问对象的类)并且通常不鼓励。不是因为它本身是错误的,而是因为它往往被滥用。尽可能依赖传统的OO原则。滥用instanceofcode smell

总而言之,我建议使用访问者进行搜索,并且不使用元编程用于表示层,而是为每种类型的车辆创建一个简单页面。

答案 1 :(得分:6)

好的,所以你要扩展类而不是实现接口。如果您有Bus,Car,Truck,Train类,并且它们都实现了IVehicle,它需要一个返回可排序/可搜索值的函数,您可以将它们全部作为类型IVehicle引用并在所有这些上调用该方法

  

ActionScript 3代码:

package com.transportation.methods {
  public interface IVehicle {
    function getSpeed():Number;
    function getOtherSortableOrSearchableValue():*;
  }
}

public class Car extends Sprite implements IVehicle

您需要在Car类中定义getSpeed()和getOtherSortableValue(),并且可以将其称为Car或IVehicle。由于我的示例中的所有传输模式都将实现IVehicle,只要您将它们作为IVehicle引用,就可以调用这两个函数。

答案 2 :(得分:4)

您似乎在描述Steve Yegge所谓的Universal design pattern.

在GUI中使用键值对可以在简单的情况下工作,但很难让它看起来很好。除非您的对象层次结构很深并且必须是非常可扩展的,否则您可能希望为每个具体类创建一个单独的表单,因为这样做的工作量较少且看起来会更好。您仍然可以重用共享的GUI组件,因此添加一个新的具体类应该相当简单。

答案 3 :(得分:2)

在我看来,这似乎是某种“集中式”存储库模式。我认为这不是一个好主意。

你建议你做的是,你应该拥有几个专门的存储库,一个用于汽车,一个用于船只,一个用于飞机等,而不是采用一种集中式的广义搜索方式。

每个存储库都知道相应对象的详细信息。然后,您可以将这些专用存储库之间的常见操作分解为Repository基类或接口。如果不关心细节,则使用基本Repository接口。如果您关心细节,请使用专门的存储库。

这是Java中一个简单的例子(我为了简短而离开了getters / setters - 让你的字段公开):

interface Vehicle { int id; int maxSpeed; }
class Car implements Vehicle { int doors; int id; int maxSpeed; }
class Boat implements Vehicle { int buoyancy; int id; int maxSpeed; }
class Plane implements Vehicle { int wingSpan; int id; int maxSpeed; }

interface VehicleRepository<T extends Vehicle> {
    T getFastest();
    T getSlowest();
    T getById(int id);
}

interface CarRepository inherits VehicleRepository<Car> {
    List<Car> getCarsWithTwoDoors();
}

interface BoatRepository inherits VehicleRepository<Boat> {
    Boat getMostBuoyantBoat();
}

interface PlaneRepository inherits VehicleRepository<Plane> {
    List<Plane> getPlanesByWingspan();
}

答案 4 :(得分:2)

这听起来像是访客模式的工作。你有一组抽象对象,你不知道每个对象是什么。使用Visitor,您可以遍历所有对象并执行特定于每个对象的操作。 GOF Patterns书提供了访问者模式的详细信息,但我会尝试在这里提供一个很好的Java示例。

public class Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public interface VehicleVisitor {
    public void visit( Vehicle vehicle );
    public void visit( Car car );
    public void visit( Bus bus );
    public void visit( Truck truck );
    // Augment this interface each time you add a new subclass.
}

public class Car extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class Bus extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class Truck extends Vehicle {
    public void accept( VehicleVisitor visitor ) {
        visitor.visit( this );
    }
}

public class VehicleSearch implements VehicleVisitor {
    protected String name;
    public List<Vehicle> foundList =
        new ArrayList<Vehicle>();
    public VehicleSearch( String name ) {
        this.name = name;
    }
    public void visit( Vehicle vehicle ) {
        return;
    }
    public void visit( Car car ) {
        if ( car.getModel().contains( name ) ) {
            foundList.add( car );
        }
    }
    public void visit( Bus bus ) {
        if ( bus.getManufacturerModel().contains( name ) ) {
            foundList.add( bus );
        }
    }
    public void visit( Truck truck ) {
        if ( truck.getLineModel().contains( name ) ) {
            foundList.add( truck );
        }
    }
}

public class Manager {
    protected List<Vehicle> vehicleList;
    public List<Vehicle> search( String name ) {
        VehicleSearch visitor =
            new VehicleSearch( name );
        for ( Vehicle vehicle : vehicleList ) {
            vehicle.accept( visitor );
        }
        return visitor.foundList;
    }
}

乍一看,是的,您可以简单地将一个搜索方法添加到Vehicle类并为列表的每个成员调用该方法,但访问者模式允许您在车辆列表上定义多个操作,这样您就不会必须为每个类的Vehicle类添加一个新方法。

访问者模式的一个缺点是,当您添加要访问的新类对象时,访问者本身需要更改。在此示例中,如果您向系统添加RocketShip工具,则需要向访问者添加visit(RocketShip rocketShip)方法。

您可以通过创建一个覆盖了所有访问方法的VehicleVisitorTemplate来缓解此问题。您的操作是模板类的子类,然后只需要覆盖所需的方法。当您需要添加新类和访问方法时,将其添加到接口和模板类中,然后除非必要,否则不需要更新所有其他操作。

答案 5 :(得分:0)

我没看到你正在使用什么语言,但为什么不让抽象类实现接口呢?这样,只要它们都从实现接口的抽象类继承,那么您拥有多少具体类别并不重要。

以下是使用Java时层次结构的外观:

public interface IVehicle {/*your code goes here*/}

public abstract class AbstractVehicle implements IVehicle{/*your code goes here*/}

public class Car extends AbstractVehicle{/*your code goes here*/}

当然,这些都将在不同的文件中定义。

答案 6 :(得分:0)

  

管理员如何使用抽象类来搜索特定项目而不会转换为汽车或公共汽车

我这样做的方法是抽象类有一个抽象方法,它返回一个ID值(比如一个枚举值),它指示对象是哪个具体类型,并让每个具体类覆盖它返回它的ID值。

或者我尝试使用C ++的RTTI工具或Python的instanceof()函数。

这有帮助,还是我回答错误的问题?

答案 7 :(得分:0)