So there is a small scenario from a big system. It is a Car Booking System where User class needs to be more cohesive. In my Initial Scenario, My User class was catering User operations, User Verification, User Profile and had a different class for Admin.
- team was instructed to make User class more cohesive
- Break User into UserProfile and make the Verification Process separate from this class
- As Admin is also a User so you can inherit Admin from User.
- Admin will inherit the UserProfile part but the Verification Process will only be applicable to customer.
now this is where I got stuck and I don't know how the User Verification can only connect to User which is parent class but not goes to the child class. as Admin don't need any verification process.
My suggested solution is:
but I am very unsure about inheriting two users and specially when the Customer class have all methods of Verification, that means Class name dont represent it's actual functionality.
CodePudding user response:
How is the design?
Your suggested solution is an interesting start. It separates:
- the
User
, which exposes operations that could be driven by some user interface, - the
UserProfile
, which manages the personal information of a user, - the user's role, i.e.
Customer
role orAdmin
, which provides role specific behaviors and responsibilities.
The main weakness of this design is an insufficient separation of concerns.
Separating user-interface behavior from internal behavior
Some of your classes seem to be domain classes but seem at the same time to be supposed to have user-interface responsibilities (e.g. UserProfile
and editProfile()
)
If this is a toy project with a command line interface, it's ok. But a safer approach would be to focus on classes that could be invoked by a user interface with some data, but without having itself any user-interface responsibility (e.g. editProfile()
would be replaced with getters that would allow a form to populate content, and setters, to update the class once the form is validated).
Separate responsibilities using the decorator pattern
Customer
and Admin
both add different sets of responsibilities and attributes to the user. Instead of using inheritance, you could prefer composition and use the decorator pattern: Customer
and Admin
would then be decorators of User
, where Customer
would add the responsibility to manage the verification.
A slight improvement would be to make the distinction between a User
and a UserRole
. A UserRole
would be an abstract class, and Customer and Admin would be decorators of the role.
This would allow you to easily introduce new roles (Supplier
, Auditor
, ...) without changing the design of the users. You could even allow multiple roles at the same time if you change multiplicity on the UserRole
side of the composition.
Implement policies with the strategy pattern
Another problem that you have introduced in your solution is that the UserVerification
was merged part into UserProfile
and part into Customer
. This is not a sound separation of concerns.
A better approach is to make UserVerification
a strategy of a UserRole
. You could decide to set the strategy in the construction of each specific UserRole
. The Admin
could then use a light strategy that considers a user as always verified without further steps.
Some more (unrelated) thoughts
Never ever store passwords. The interaction should not be: "ask password, then get profile password and verify if they are the same", but "ask password, compute a password hash code, check if the hash code matches".
In this regard, you may consider extracting password related properties and operations into a separate class. This would allow to change user authentication strategies exists and opt of OS authentication, 2FA authentication, single sign on, and other authentication approaches.