“Use Case: When you need to return an alternate object of the same base type which will controll the flow of the application. Example: UserServiceClient can return a UserNotFound object instead of throwing a custom UserServiceException or having serialization exception caught in the consumer of the service.”
Definition
“A subclass that provides special behavior for particular cases.” - martinfowler.com
Example
Consider the following UserServiceClient with method GetUserAsync
1 | public async Task<UserDto> GetUserAsync(Guid userId, CancellationToken cancellationToken) |
The return type UserDto is a simple data transfer object.
1 | public class UserDto |
If the service returns 404 with no content this will throw JsonException as content will be null. We can fix this with the following steps:
- Add a virtual method to
UserDtocalledFound()and return true
1 | public class UserDto |
- Create the
UserNotFoundobject that inherits fromUserDtoand overrides theFoundmethod which can then return false
1 | public class UserNotFound : UserDto |
- In the
GetUserAsyncmethod insideUserServiceClientcheck the status code. Here we assume 404 (not found) but you could check for any other code like 500 and respond with eitherUserServiceExceptionor another object likeUserError
1 | public async Task<UserDto> GetUserAsync(Guid userId, CancellationToken cancellationToken) |
- In the consumer we would then check the response
1 | var user = await _userServiceClient.GetUserAsync(userId, cancellationToken) |
This code now has some defensive checks using the Special Case Pattern.