I have the following class hierarchy for Coupon and Deals platform am developing::
Promotion - abstract
- Coupon
- Sale
- Deal
(Coupon, Sale and Deal inherit from Promotion. Promotion has a string attribute called type
and an abstract method that initializes the the type attributes of the subclasses to a string value. For instance the type
in coupon gets the value "Coupon" etc...)
For each subclass, I have a DAO and Service classes like CouponDAO
, CouponService
, etc.
In the front-end users can create Coupon or Sale or a Deal through Angular 2 interface so I decided to have the following controllers:
PromotionController - abstract
- CouponController
- SaleController
- DealController
(CouponController, SaleController, DealController inherit from PromotionController )
The PromotionController
will contain all the common CRUD functions common to all subclasses and in the specific controllers I will handle specific operations meant for those classes.
A) The issue am facing now is how to instantiate the correct object coming from the client side. For instance when a user submit a Coupon or a Sale or a Deal how do I instantiate the right object. For instance in the PromotionController I have a function like this::
@RequestMapping(value=CREATE_PROMO, method=RequestMethod.POST)
public ResponseEntity<?> create(@RequestBody Promotion promotion){
promotionService.save(promotion);
return new ResponseEntity<>("", HttpStatus.OK);
}
Promotion which is abstract is the argument of the function. Should I use the factory pattern and the **type**
attribute to create the right object?
For instance if the type="Coupon" then I create Coupon object, if it is "Sale" then I create the Sale object
B) Since the controller uses the Services objects it means that I have to declare all the three services objects in the PromotionController. Because after instantiating the right object, I need to call its corresponding service to do the job. In the method above I have promotionService which I think should be replaced with the right service of subclass
C) I am looking for how to handle REST APIs that deals with subclasses in the real world like the situation I have described above
D) I was thinking of making it easy for myself by copying all the CRUD operations to their specific controllers but it seems that will be repetitive code.
I think there is a better way that can be done.
I have also tried if I can find an open source project that deals with this situations but it seems all the projects I found use one class and not inheritance. Their REST/APIs don't handle inheritance situations
A subclass does not inherit the private members of its parent class. However, if the superclass has public or protected methods for accessing its private fields, these can also be used by the subclass. A nested class has access to all the private members of its enclosing class—both fields and methods.
Subclasses inherit public methods from the superclass that they extend, but they cannot access the private instance variables of the superclass directly and must use the public accessor and mutator methods. And subclasses do not inherit constructors from the superclass.
Subclass methods can call superclass methods if both methods have the same name. From the subclass, reference the method name and superclass name with the @ symbol.
RestController is a Spring annotation that is used to build REST API in a declarative way. RestController annotation is applied to a class to mark it as a request handler, and Spring will do the building and provide the RESTful web service at runtime.
In my view, keep your endpoints simple. From a REST API standpoint, create individual or only one controller and use the following patterns after the controller layer. From what I have seen, it is always better to keep REST endpoints away from inheritance/reuse and apply it later after receiving and validating the requests.
To instantiate service/helper layer from controllers, use factory method pattern:
https://en.wikipedia.org/wiki/Factory_method_pattern
Create a PromotionServiceFactory which returns the PromotionService implementation depending upon the promotion type.
In controller, invoke corresponding method of promotion service using the factory. The factories still accept arguments of type Promotion.
@RequestMapping(value=CREATE_COUPON, method=RequestMethod.POST)
public ResponseEntity<?> create(@RequestBody Promotion promotion){
//helper if adding one more helper layer. The factory invocation is then //transferred to the helper layer
PromotionService couponService = promotionServiceFactory.get(PROMOTYPES.COUPON);
couponService.save(promotion);
return new ResponseEntity<>("", HttpStatus.OK);
}
From your questions, it seems like that there are common CRUD/other methods for different promotion types. This is a good candidate of the template pattern in the service layer if some of the steps/sub-tasks are same for every promotion and the others vary. Otherwise, you could just store the common CRUD methods by creating an abstract promotion service.
https://en.wikipedia.org/wiki/Template_method_pattern
Create an abstract promotion service with the primary method and implementations of common CRUD methods. Create individual implementations of other promotion service types with respective varying methods.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With