Hi folks,
I have a question about an exercise from the "Implementing an Object-Oriented Design Pattern" in the TRPL book. In chapter 17, after the Blog
app is refactored to type-based state transitions, the reader is asked to implement an additional requirement:
Require two calls to approve before the state can be changed to
Published
.
The way I understand the programming model, it means that the approve
function of the PendingReviewPost
should return either PendingReviewPost
or Post
, depending on a number of approvals:
pub struct PendingReviewPost {
content: String,
num_of_approvals: usize,
}
impl PendingReviewPost {
pub fn approve(self) -> Post {
if self.num_of_approvals == 1 {
Post {
content: self.content,
}
} else {
self.num_of_approvals += 1;
self
}
}
}
But function can have only one return type.
Option 1
As it was pointed out in Discord, it would be possible to solve this by adding more interim types and more functions:
impl PendingReviewPost {
pub fn add_first_approval() -> PostWithOneApproval
pub fn add_second_approval() -> ApprovedPost
}
but it's not something that I would typically do because:
- That would expose client code to unnecessary implementation details and eventually will lead to a coupling between supposedly independent parts of the codebase
-
PendingPost
whether with zero approvals or one approval is still aPendingPost
. Next software developer to read that code would scratch his head trying to understand the rationale behind that design.
2.1 There would be a necessity for a mechanism to share common behaviour between those two types.
Option 2
Another approach would be to introduce an enum to capture either one of those outcomes:
enum EitherPreApprovedOrApproved {
PreApproved(PendingReviewPost),
Approved(Post),
}
pub fn approve(self) -> EitherPreApprovedOrApproved {
if self.num_of_approvals == 1 {
EitherPreApprovedOrApproved::Approved(Post {
content: self.content,
})
} else {
self.num_of_approvals += 1;
EitherPreApprovedOrApproved::PreApproved(self)
}
}
But again, this code would put a client in the unfavourable position of extracting value from approve()
and basically putting onto it a burden of dealing with state transitions.
So I believe there should be other solutions to this problem. Would anyone please enlighten me with an idiomatic solution to this problem?
Thank you and Happy New Year !