Implementeer het Template Method Ontwerppatroon in mijn REST API
description
Door het gebruik van het Template Method Ontwerppatroon kan ik ervoor zorgen dat mijn API een duidelijke structuur behoudt terwijl ik aanpassingen kan maken waar nodig, wat de onderhoudbaarheid van de code verbetert en redundantie vermindert.
prompt
try_prompt
Help me het Template Method Ontwerppatroon implementeren in mijn REST API om de structuur van een algoritme te definiëren terwijl ik bepaalde stappen kan herdefiniëren zonder de structuur van het algoritme te veranderen. Mijn API-doel is {{api_purpose}}. Beschrijf ... more
generate_helper
...
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Zeker! Het Template Method ontwerp patroon is ideaal om een vaste structuur te definiëren voor je API-verzoeken, terwijl je bepaalde stappen flexibel kunt aanpassen. Hieronder leg ik uit hoe je dit patroon kunt toepassen in een REST API voor gebruikersbeheer, inclusief voorbeeldstructuur en implementatie.
### 1. Conceptueel overzicht
- **Abstracte klasse (Template):** Definieert de skeleton van een bewerking (bijvoorbeeld: verifiëren, verwerken, opslaan).
- **Concrete klassen:** Implementeren de specifieke stappen die per endpoint variëren.
- **API Endpoints:** Roepen de juiste concrete implementatie aan, maar blijven binnen de vaste structuur.
---
### 2. Structuur van je API Endpoints
Je kunt een gemeenschappelijk controller- of service-abstractie maken die de algemene flow definieert. Elke specifieke bewerking (bijv. gebruikersregistratie, profielupdate, wachtwoordreset) implementeert de variabele stappen.
#### Voorbeeld: Basis abstracte klasse
```java
public abstract class UserAccountOperation {
// De template methode die de structuur bepaalt
public final void execute() {
verifyRequest();
processRequest();
saveChanges();
sendResponse();
}
protected void verifyRequest() {
// Standaard verificatie
// Kan worden overschreven
}
protected abstract void processRequest();
protected void saveChanges() {
// Standaard opslaan, kan worden uitgebreid
}
protected void sendResponse() {
// Standaard response verzenden
}
}
```
---
### 3. Concrete implementaties
Voor elke API-endpoint maak je een subclass:
#### Voorbeeld: Nieuwe gebruiker aanmaken
```java
public class CreateUserOperation extends UserAccountOperation {
private UserData data;
private User createdUser;
public CreateUserOperation(UserData data) {
this.data = data;
}
@Override
protected void verifyRequest() {
// Specifieke verificatie voor nieuwe gebruiker
if (data.getEmail() == null || data.getPassword() == null) {
throw new IllegalArgumentException("Email en wachtwoord verplicht");
}
}
@Override
protected void processRequest() {
// Logica voor gebruiker aanmaken
createdUser = new User(data.getEmail(), data.getPassword());
}
@Override
protected void saveChanges() {
// Opslaan in database
userRepository.save(createdUser);
}
@Override
protected void sendResponse() {
// Standaard of aangepaste response
System.out.println("Gebruiker succesvol aangemaakt: " + createdUser.getId());
}
}
```
---
### 4. Integratie in je REST API
In je controller:
```java
@PostMapping("/users")
public ResponseEntity<?> createUser(@RequestBody UserData data) {
UserAccountOperation operation = new CreateUserOperation(data);
try {
operation.execute();
return ResponseEntity.ok("Gebruiker aangemaakt");
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
```
Voor andere endpoints (bijv. profielupdating):
```java
public class UpdateProfileOperation extends UserAccountOperation {
private Long userId;
private UserData newData;
public UpdateProfileOperation(Long userId, UserData newData) {
this.userId = userId;
this.newData = newData;
}
@Override
protected void verifyRequest() {
// Controleer of gebruiker bestaat
if (!userRepository.existsById(userId)) {
throw new IllegalArgumentException("Gebruiker niet gevonden");
}
}
@Override
protected void processRequest() {
// Laad bestaande gebruiker en update
User user = userRepository.findById(userId);
user.setProfileData(newData);
// andere verwerking
}
@Override
protected void saveChanges() {
userRepository.save(user);
}
}
```
---
### 5. Samenvatting
- Definieer een abstracte klasse die de structuur van je algoritme vastlegt (`execute()`).
- Maak subclasses voor elke specifieke actie die de stappen aanpassen.
- In je API controllers roep je de `execute()` methode aan, waardoor de structuur behouden blijft en hergebruik mogelijk is.
### Voordelen
- **Herbruikbaarheid:** Gemeenschappelijke stappen (zoals validatie, opslaan, responses) worden gedeeld.
- **Flexibiliteit:** Specifieke stappen kunnen worden aangepast per endpoint.
- **Onderhoud:** Veranderingen in de structuur hoeven slechts op één plek te worden doorgevoerd.
---
Heeft u meer details nodig over een specifieke implementatie of framework?
provider: deepseek
model: deepseek-chat
Zeker, ik help je graag het Template Method Ontwerppatroon te implementeren in je REST API voor gebruikersaccountbeheer. Hier is een uitgebreide beschrijving van hoe je dit kunt aanpakken:
## API Structuur met Template Method Pattern
### Basis Endpoint Structuur
```
POST /users/register # Gebruiker registreren
PUT /users/{id}/profile # Profiel bijwerken
GET /users/{id}/profile # Profiel ophalen
DELETE /users/{id} # Gebruiker verwijderen
```
## Abstracte Basisklasse
```java
public abstract class UserManagementTemplate {
// Template method - definieert het algoritme
public final ResponseEntity<?> executeUserOperation(UserRequest request) {
try {
// 1. Validatie stap
validateRequest(request);
// 2. Authenticatie/autorisatie stap
authenticateAndAuthorize(request);
// 3. Business logica stap (abstract - moet geïmplementeerd worden)
Object result = performBusinessLogic(request);
// 4. Post-verwerking stap
postProcess(result);
// 5. Response bouwen
return buildSuccessResponse(result);
} catch (ValidationException e) {
return buildValidationErrorResponse(e);
} catch (AuthorizationException e) {
return buildAuthorizationErrorResponse(e);
} catch (Exception e) {
return buildErrorResponse(e);
}
}
// Gemeenschappelijke stappen (kunnen worden overgenomen of overschreven)
protected void validateRequest(UserRequest request) {
// Basis validatie logica
if (request == null) {
throw new ValidationException("Request mag niet null zijn");
}
}
protected void authenticateAndAuthorize(UserRequest request) {
// Basis authenticatie/autoristatie logica
// Bijv. JWT token validatie
}
protected void postProcess(Object result) {
// Standaard post-verwerking
// Bijv. logging, metrics, caching
System.out.println("Operatie succesvol voltooid: " + result);
}
protected ResponseEntity<?> buildSuccessResponse(Object result) {
return ResponseEntity.ok(new ApiResponse("success", result));
}
protected ResponseEntity<?> buildErrorResponse(Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ApiResponse("error", e.getMessage()));
}
// Abstracte methoden die moeten worden geïmplementeerd door subklassen
protected abstract Object performBusinessLogic(UserRequest request);
protected abstract ResponseEntity<?> buildValidationErrorResponse(ValidationException e);
protected abstract ResponseEntity<?> buildAuthorizationErrorResponse(AuthorizationException e);
}
```
## Concrete Implementaties
### 1. Gebruiker Registratie
```java
@Component
public class UserRegistrationHandler extends UserManagementTemplate {
@Override
protected Object performBusinessLogic(UserRequest request) {
User user = new User();
user.setEmail(request.getEmail());
user.setPassword(hashPassword(request.getPassword()));
user.setProfile(new UserProfile());
// Opslaan in database
User savedUser = userRepository.save(user);
// Verstuur welkomstemail
emailService.sendWelcomeEmail(savedUser.getEmail());
return savedUser;
}
@Override
protected void validateRequest(UserRequest request) {
super.validateRequest(request);
// Specifieke validatie voor registratie
if (request.getEmail() == null || !isValidEmail(request.getEmail())) {
throw new ValidationException("Ongeldig emailadres");
}
if (request.getPassword() == null || request.getPassword().length() < 8) {
throw new ValidationException("Wachtwoord moet minimaal 8 karakters bevatten");
}
if (userRepository.existsByEmail(request.getEmail())) {
throw new ValidationException("Emailadres is al in gebruik");
}
}
@Override
protected void authenticateAndAuthorize(UserRequest request) {
// Registratie vereist geen authenticatie - overschrijf basisgedrag
// Geen authenticatie nodig voor nieuwe gebruikers
}
@Override
protected ResponseEntity<?> buildValidationErrorResponse(ValidationException e) {
return ResponseEntity.badRequest()
.body(new ApiResponse("validation_error", e.getMessage()));
}
@Override
protected ResponseEntity<?> buildAuthorizationErrorResponse(AuthorizationException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(new ApiResponse("authorization_error", e.getMessage()));
}
private String hashPassword(String password) {
// Wachtwoord hashing logica
return passwordEncoder.encode(password);
}
}
```
### 2. Profiel Bijwerken
```java
@Component
public class ProfileUpdateHandler extends UserManagementTemplate {
@Override
protected Object performBusinessLogic(UserRequest request) {
User user = userRepository.findById(request.getUserId())
.orElseThrow(() -> new RuntimeException("Gebruiker niet gevonden"));
UserProfile profile = user.getProfile();
profile.setFirstName(request.getFirstName());
profile.setLastName(request.getLastName());
profile.setBio(request.getBio());
profile.setAvatarUrl(request.getAvatarUrl());
User updatedUser = userRepository.save(user);
// Cache bijwerken
cacheService.updateUserProfile(updatedUser.getId(), profile);
return updatedUser.getProfile();
}
@Override
protected void validateRequest(UserRequest request) {
super.validateRequest(request);
if (request.getUserId() == null) {
throw new ValidationException("User ID is vereist");
}
}
@Override
protected void authenticateAndAuthorize(UserRequest request) {
// Specifieke autorisatie voor profiel bijwerken
String currentUserId = securityContext.getCurrentUserId();
if (!currentUserId.equals(request.getUserId())) {
throw new AuthorizationException("Niet geautoriseerd om dit profiel te wijzigen");
}
}
@Override
protected ResponseEntity<?> buildValidationErrorResponse(ValidationException e) {
return ResponseEntity.badRequest()
.body(new ApiResponse("profile_validation_error", e.getMessage()));
}
@Override
protected ResponseEntity<?> buildAuthorizationErrorResponse(AuthorizationException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new ApiResponse("profile_update_unauthorized", e.getMessage()));
}
}
```
## Controller Implementatie
```java
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRegistrationHandler userRegistrationHandler;
@Autowired
private ProfileUpdateHandler profileUpdateHandler;
@PostMapping("/register")
public ResponseEntity<?> registerUser(@RequestBody UserRegistrationRequest request) {
return userRegistrationHandler.executeUserOperation(request);
}
@PutMapping("/{userId}/profile")
public ResponseEntity<?> updateProfile(@PathVariable String userId,
@RequestBody ProfileUpdateRequest request) {
request.setUserId(userId);
return profileUpdateHandler.executeUserOperation(request);
}
}
```
## Ondersteunende Klassen
```java
// Basis request klasse
public abstract class UserRequest {
private String userId;
private String email;
// getters en setters
}
// Specifieke request klassen
public class UserRegistrationRequest extends UserRequest {
private String password;
private String firstName;
private String lastName;
// getters en setters
}
public class ProfileUpdateRequest extends UserRequest {
private String firstName;
private String lastName;
private String bio;
private String avatarUrl;
// getters en setters
}
// Response wrapper
public class ApiResponse {
private String status;
private Object data;
private String message;
public ApiResponse(String status, Object data) {
this.status = status;
this.data = data;
}
// getters en setters
}
```
## Voordelen van deze aanpak:
1. **Hergebruik van code**: Gemeenschappelijke logica (validatie, error handling) wordt gedeeld
2. **Flexibiliteit**: Specifieke stappen kunnen worden aangepast per operatie
3. **Onderhoudbaarheid**: Algoritmestructuur is centraal gedefinieerd
4. **Uitbreidbaarheid**: Nieuwe operaties kunnen eenvoudig worden toegevoegd
5. **Consistentie**: Alle endpoints volgen dezelfde flow
Deze structuur zorgt voor een schaalbare en onderhoudbare API waarbij je eenvoudig nieuwe gebruikersgerelateerde operaties kunt toevoegen zonder de algemene structuur te wijzigen.

