如何使用两种不同类型的用户组织OO设计

时间:2011-05-15 03:35:26

标签: java oop design-patterns

我有两种不同类型的用户,我已将它们映射到两个Java类 UserWheel UserSea ,并且它们有一个名为的常见抽象超类用户即可。为这些用户类型保存的数据大致相同,但行为不同。

然后我创建了一个名为 UserCollection 的抽象类,派生类 UserWheelCollection UserSeaCollection 来搜索子用户或加载子用户。

然后我在UserCollection类中添加了一个带签名

的抽象方法
public abstract List<User> listAllSubusers()

这是因为实施方式会有所不同。创建的每个用户都将是UserWheel或UserSea,具体取决于调用的方法,但实现的其余部分也完全不同。

然后我想用签名 public User loadById(int idUser)向UserCollection添加一个新方法。在这种情况下,实现将是相同的,除了用户返回的事实将是 UserWheel UserSea 的实例。在这种情况下,我不愿意在基类中使用抽象方法,因为代码重复。

我可以使用 instanceof 检查UserCollection的具体类,并创建一个合适的子类,但它似乎不是面向对象的,并打破了开放式原则。

另一个想法是向UserCollection添加一个抽象方法 createNewUser(),并在子类中添加具体实现来返回一个新实例,因此基类只会调用这个createNewUser()方法。 / p>

你认为第二条道路有意义吗?或者你会以不同的方式组织事情以及如何组织?


更新即可。目前的情况是:

abstract class User
   public String getAddress()
   public void setAddress()
   ...

class UserSea extends User
class UserWheel extends User

abstract class UserCollection
   protected abstract User createNewUser();
   public abstract List<User> listAllSubUsers();
   public User loadById(int idUser) {
       User newUser = createNewUser();
       //populate it
       return newUser;
   }

class UserSeaCollection
   protected User createNewUser() {
        return new UserSea();
   }
   public List<User> listAllSubusers()

class UserWheelCollection
   protected User createNewUser() {
       return new UserWheel();
   }
   public List<User> listAllSubusers()

我试图了解策略模式,正如trashgod所建议的那样,这是我的第一次尝试:

interface SubuserManagement
    List<User> listAllSubUsers();
    ...

interface UserCrud
   void create();
   User readById(int idUser);
   void update();
   void delete();

class UserSeaCollection implements SubUserManagement, UserCrud

   private SubUserManagement subuserBehavior = new SubUserManagementSeaImplementation();
       private UserCrud userCrudBehavior = new UserCrud();

   void create {
       subUserBehavior.create();
   }
   ...

class UserWheelCollection implements SubUserManagement, UserCrud
       ...

class SubUserManagementWheelImplementation implements SubUserManagement
    List<User> listAllSubUsers();

class SubUserManagementSeaImplementation implements SubUserManagement
    List<User> listAllSubUsers();

class UserCrudImplementation implements UserCrud //only 1 implementation
   void create();
   User readById(int idUser);
   void update();
   void delete();

在第一次尝试中,我创建了UserCollectionWheel和UserCollectionSea,它们不再共享一个公共超类,但实现了相同的接口。实际的实现是在外部类中。

现在,UserCollectionWheel和UserCollectionSea实际上是同一个类,唯一不同的是我分配给它们的行为。或者,我只能用setter编写一个类:

UserCollection userColl = new UserCollection();
userColl.setSubUserBehavior(new SubUserManagementSeaImplementation());
userColl.setCrudBehavior(new UserCrud());

但是初始化会很麻烦,特别是如果我有更多的行为类。那么我做错了什么?如何妥善组织?

更新2 :我用我实施的设计写了blog post

3 个答案:

答案 0 :(得分:3)

不要继承行为,而是考虑使用strategy pattern中的接口封装它。用户在使用interface ListSubUsersStrategyinterface CreateUserStrategy等两种具体实现方式时会有所不同。

另请参阅相关的bridge pattern

附录:在下面的示例中,每个用户都有一个查找子用户的具体策略。特别是,listAllSubUsers()调用接口方法,自动分派到正确的具体实现。该模式并不能减轻您编写接口的具体实现,但它会解除它们,确保更改它不会破坏另一个。

控制台:

A has wheel users.
B has sea users.
C has wheel users.

代码:

import java.util.ArrayList;
import java.util.List;

/** @see http://stackoverflow.com/questions/6006323 */
public class UserMain {

    private static final List<User> users = new ArrayList<User>();

    public static void main(String[] args) {
        users.add(new User("A", new WheelStrategy()));
        users.add(new User("B", new SeaStrategy()));
        users.add(new User("C", new WheelStrategy()));
        for (User user : users) {
            user.listAllSubUsers();
        }
    }

    private static class User {

        private String name;
        private SubUsersStrategy suStrategy;

        public User(String name, SubUsersStrategy suStrategy) {
            this.name = name;
            this.suStrategy = suStrategy;
        }

        public void listAllSubUsers() {
            System.out.print(name + " manages ");
            List<User> subUsers = suStrategy.getList();
        }
    }

    private interface SubUsersStrategy {

        List<User> getList();
    }

    private static class WheelStrategy implements SubUsersStrategy {

        @Override
        public List<User> getList() {
            System.out.println("wheel users.");
            return null;
        }
    }

    private static class SeaStrategy implements SubUsersStrategy {

        @Override
        public List<User> getList() {
            System.out.println("sea users.");
            return null;
        }
    }
}

答案 1 :(得分:3)

FWIW,这是我对Trashgod战略模式方法的看法。这不是答案,只是对Trashgod答案的支持性解释。

public interface UserStore<T extends User> {
    public T create(int id);
    public List<T> listAll();
}

public class SeaUserStore implements UserStore<SeaUser> {
    public SeaUser create(int id) { return new SeaUser(id); }
    public List<SeaUser> listAll() { whatever }
}

// the dry counterpart of 'sea' is either 'land' or 'road', not 'wheel' :)
public class RoadUserStore implements UserStore<RoadUser> {
    public RoadUser create(int id) { return new RoadUser(id); }
    public List<RoadUser> listAll() { whatever }
}

public class UserCollection<T extends User> {
    private UserStore<T> store;
    public UserCollection(UserStore<T> store) {
        this.store = store;
    }
    public List<T> listAll() {
        return store.listAll();
    }
    public T getById(int id) {
        T user = store.create(id);
        // populate
        return user;
    }
}

这样就可以让客户端创建UserCollection。你可以把它包起来;将UserCollection构造函数设为私有,并添加:

public class UserCollection<T extends User> {
    public static UserCollection<SeaUser> createSeaUserCollection() {
        return new UserCollection<SeaUser>(new SeaUserStore());
    }
    public static UserCollection<RoadUser> createRoadUserCollection() {
        return new UserCollection<RoadUser>(new RoadUserStore());
    }
}

答案 2 :(得分:1)

  

我可以使用instanceof检查UserCollection的具体类,并创建一个合适的子类,但它似乎不是面向对象的,并打破了开放原则。

这会打破 Liskovs替换原则,这实际上是检查一个人是否具有声音对象层次结构的规则。规则说如果方法需要User作为参数,那么用户是CompledUserDirtyUser还是任何其他用户都无关紧要。 (因此,您可能没有任何if检查实例等。)

继承是关于“is-a”关系的,这基本上意味着你应该能够获取任何派生对象并将其传递给期望基类的方法。如果你不能这样做,你就会遇到很多麻烦。这样的问题是因为你的课程失败了。

  

另一个想法是将一个抽象方法createNewUser()添加到UserCollection,并在子类中添加具体实现以返回一个新实例,因此基类只会调用这个createNewUser()方法

这是一种更好的方法,因为调用者如果createNewUser不关心它获得哪种类型的用户。它只知道它是一个用户。

该方法称为工厂方法模式。