- 1,229
- 0
Last Spring our team had a problem designing one part of our plugin (Java, but what's relevant is that it is OOP). We had to store a lot of users, who are organized into sessions. The sessions are in turn organized into courses, and all the courses have to be stored somewhere also. The solution we used, which was mostly satisfactory, was:
1. The singleton UserList contains a flat list of all users, and was originally intended to be the single point of access to user operations
2. The singleton CourseList contains a list of all Courses
3. Each Course contains a list of Sessions for that course
4. Each Session contains a list of users in that session
The problem is that there are operations that need to be done at various levels in this hierarchy. For example, if you log a user out of an individual Session, this could be done at the Course level, since the Session the user was in might become empty and have to be removed. But it could also be done at the UserList level, which originally was going to be an abstraction for the whole framework. The laziest thing to do is make the operation available at the lowest level of the hierarchy that it could be, while still allowing for all the necessary operations. But it does not seem right that the user of the framework should be required to guess the correct level. For example, they might try u.getCourse().removeUserFromSession(); or they might try u.getSession().removeUserFromSession(); or they might try CourseList.getInstance().removeUserFromSession(u); - if they hadn't designed it, it wouldn't be obvious which was the right choice. This looks like a problem of insufficient data hiding. On the other hand, a flat design with everything at the UserList level can too quickly become bloated--had we flattened the design so that , UserList would have had about 30 methods in it. Also, a top-level UserList.getInstance().removeUserFromSession(u) would have to in turn call a protected method like u.getCourse().removeUserFromSession(), so it is sort of redundant. What we settled on doing was basically a haphazard setup, with methods available wherever we happened to think of when writing them originally, and anyone who wanted to use the framework had to be moderately familiar with it.
Is there a formal design pattern which handles this situation? The one that sounded the most promising was "chain of responsibility" but that turned out not to apply very closely.
1. The singleton UserList contains a flat list of all users, and was originally intended to be the single point of access to user operations
2. The singleton CourseList contains a list of all Courses
3. Each Course contains a list of Sessions for that course
4. Each Session contains a list of users in that session
The problem is that there are operations that need to be done at various levels in this hierarchy. For example, if you log a user out of an individual Session, this could be done at the Course level, since the Session the user was in might become empty and have to be removed. But it could also be done at the UserList level, which originally was going to be an abstraction for the whole framework. The laziest thing to do is make the operation available at the lowest level of the hierarchy that it could be, while still allowing for all the necessary operations. But it does not seem right that the user of the framework should be required to guess the correct level. For example, they might try u.getCourse().removeUserFromSession(); or they might try u.getSession().removeUserFromSession(); or they might try CourseList.getInstance().removeUserFromSession(u); - if they hadn't designed it, it wouldn't be obvious which was the right choice. This looks like a problem of insufficient data hiding. On the other hand, a flat design with everything at the UserList level can too quickly become bloated--had we flattened the design so that , UserList would have had about 30 methods in it. Also, a top-level UserList.getInstance().removeUserFromSession(u) would have to in turn call a protected method like u.getCourse().removeUserFromSession(), so it is sort of redundant. What we settled on doing was basically a haphazard setup, with methods available wherever we happened to think of when writing them originally, and anyone who wanted to use the framework had to be moderately familiar with it.
Is there a formal design pattern which handles this situation? The one that sounded the most promising was "chain of responsibility" but that turned out not to apply very closely.