Design for a shop - multiple product classes - java

I was reading about Spring and encountered an example consisting of an abstract product class with name and price fields.
Next, there is a Battery class which extends the Product class and adds a rechargable field. Then, a CDDrive class (also) extending Product but adding a capacity field.
In the real world when we often have products having many disparate attributes, how does one model arbitrary products with arbitrary properties and fields?
Does having a class for each product make sense?
So, can you guys please suggest a pattern for achieving this?
Thanks,
Ouney

Good question. We had a similar situation before where we had GUI components that shared many of their abstract parent, but each page had its own set of labels that weren't shared by others. The standoff was on. We found it silly to just keep creating subclasses because of the mutually disjoint properties they had. What did it for us was maps. First, to have a subclass is to have one or more distinguishing properties that are fist class objects. Rechargeable for batteries and capacity for cd drives in your case. Then for the properties one can't think of at the time of building, or simply differ in minor naming conventions, use maps. I demonstrate with the example below.
The product:
public abstract class Product {
String name;
Double price;
Map<String, Object> propMap;
public Product(String name, Double price) {
this.name = name;
this.price = price;
propMap = new HashMap<>();
}
public void add2propMap(String key, Object value) {
propMap.put(key, value);
}
public String toString() {
return "Product [name=" + name + ", price=" + price + ", propMap=" + propMap + "]";
}
}
The CdDrive:
public class CdDrive extends Product {
String capacity;
public CdDrive(String name, Double price, String capacity) {
super(name, price);
this.capacity = capacity;
}
}
The Battery:
public class Battery extends Product {
Boolean rechargable;
public Battery(String name, Double price, Boolean rechargable) {
super(name, price);
this.rechargable = rechargable;
}
}
Then a client:
public class Client {
public static void main(String[] args) {
List<Product> productList = new ArrayList<>();
Battery energizer = new Battery("Energizer", 12d, true);
energizer.add2propMap("numInPackage", new Integer(8));
energizer.add2propMap("make", "US");
productList.add(energizer);
CdDrive superDrive = new CdDrive("Apple Drive", 200d, "200 GB");
superDrive.add2propMap("type", "External");
superDrive.add2propMap("expandable", false);
productList.add(superDrive);
productList.forEach(p -> System.out.println(p));
}
}
Which gives this when run:
Product [name=Energizer, price=12.0, propMap={numInPackage=8, make=US}]
Product [name=Apple Drive, price=200.0, propMap={expandable=false, type=External}]
This setup made the architecture scalable, maintainable and modifiable. The map keys always reported what was in there in case in doubt. Adding is easy and so is modifying.

Does having a class for each product make sense?
In real life situation, it rarely makes sense. They are just making up some example to make you get the feeling of it.
Just imagine your online shop sells CD players, now you want to add some MD players in your product list, and you need to change your code and redeploy the application just because of it. Non-sense huh?
Unless you have bunch of specific function for some specific type of product, having a dedicated class for such type of product will make sense. (e.g. Product, PhysicallyDeliverableProduct something like that. Still there are better way to design it though)
In real life, the way to solve the issue in your question, is mostly by designing your Product to keep some arbitrary properties (e.g. keeping a Map<String,Object>, so you can put ["rechargeable", true] for a battery you add on your site.
Design pattern? I think what you are looking for is still far from required to make use of patterns. Personally I will suggest you to take a look on the book "Analysis Pattern" by Martin Fowler. You may not be able to use the design in it directly, but it give you feel on what real life design looks like

Does having a class for each product make sense?
To me it absolutely makes sense to have separate classes for separate products.
That makes your code more loosely coupled. In future if you want to change the implementation of a particular product, changing the code won't mess up the implementation of other products if you have a separate class for that. The generic methods & properties you can put in an abstract class.
a pattern for achieving this?
You might want to look at the Factory & template pattern.
You can create an interface Product & all the classes will implement that interface & define their own implementations.
Use abstract class only when you want to provide a default behaviour to your methods. For an instance have a look at the template pattern here.
An abstract class game is created which defines the play method. initialize & startPlay etc can have their respective definition in the subclasses but the play method will always run the other methods.
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//template method
public final void play(){
//initialize the game
initialize();
//start game
startPlay();
//end game
endPlay();
}
}
If you don't intend to provide any default behaviour rather just declare the properties & methods in an interface Product & let the classes implement that.
interface Product{
String NAME="defaultName";
Integer PRICE=5;
initialCost(); // example of a generic method
}
//Note that name & price if you declare those in interface will be treated as constants.
class Battery implements Product{
Boolean rechargable =false;
public void initialCost(){
//method definition
}
}
class CdDrive implements Product{
Integer capacity = xxxx;
public void initialCost(){
//CdDrive method definition
}
}
You can create the objects as
Product product = new Battery();
Product nextProduct = new CdDrive();
this makes your code loosely coupled. Also known as programming to an interface.

Related

How we can remove code redundancy/ improve code by using generics/ any other way [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
An example of my problem is shown below. Both classes Car and Bike have the same fields (wheel and bodyColor).
In class AssembleVehicle, the method getDetails is almost duplicated for Car and Bike. Is there any way to remove redundancy or improve coding standards?
class Vehicle {
Car car;
Bike bike;
//setter & getter
}
class Car {
String wheel;
String bodyColor;
//setter & getter
}
class Bike {
String wheel;
String bodyColor;
//setter & getter
}
class AssembleVehicle {
public void init(Vehicle v) {
getDetails(v.getCar);
getDetails(v.getBike);
}
private void getDetails(Car c) {
String wheel = c.wheel;
String bodyColor = c.bodyColor;
}
private void getDetails(Bike c) {
String wheel = c.wheel;
String bodyColor = c.bodyColor;
}
}
Here, how we can remove code redundancy for getDetails?. Can we use this method only once?
What I understand about generics is: generics allow you to customize a "generic" method or class to whatever type you're working with. For example, suppose you have a method that adds two numbers together. In order to work with the types themselves, you might have to create multiple versions of this method.
Thinking about OOP in this way places the wrong emphasis. Even if you fix your initial mishap of having a Vehicle with a car and a bike inside of it, the 'improved' model of the Vehicle superclass with the wheel and bodyColor fields is probably broken (depending on the actual domain you are working in). For instance bike wheels and car wheels are very different, and a sleigh is a vehicle without any wheels. Things that look similar at first glance are not always good candidates to pull up into a super class.
The problem is that we are tempted to assume that if two examples of a category share some common traits, that these traits are present for all the members of this category (BTW this tendency to generalize might also be the cause of many problems in society).
The power of OOP does not come from extracting super classes like the Vehicle and 're-using' some fields. Direct subclassing is a very drastic measure that should not be applied lightly, because it means all present and future subclasses MUST inherit the complete contract (data and behavior) of the super class. As soon as you want to add a new subclass and are forced to make exceptions you know the model is wrong. And at that point changing it will impact all the existing sub classes. In fact, direct subclassing is nearly always a violation of the open-closed principle, the second in the set of SOLID design principles.
A much more flexible approach is to extract interfaces to encapsulate certain aspects of a category of classes:
interface Wheeled {
String getWheels();
}
class Car implements Wheeled {
private String wheels;
#Override
public String getWheels() {
return wheels;
}
}
class Bike implements Wheeled {
private String wheels;
#Override
public String getWheels() {
return wheels;
}
}
This would allow treating Cars and Bikes the same, if you are only interested in wheels:
List<Wheeled> wheeledVehicles = new ArrayList<>();
wheeledVehicles.add(new Car());
wheeledVehicles.add(new Bike());
String firstWheels = wheeledVehicles.get(0).getWheels());
If you are interested in body color, or even wheels AND body color, you are free to play with interfaces:
interface Wheeled {
String getWheels();
}
interface Coloured{
String getBodyColour();
}
interface WheeledAndColoured extends Wheeled, Coloured {}
Which allows:
List<WheeledAndColoured> wheeledAndColouredVehicles = new ArrayList<>();
wheeledAndColouredVehicles.add(new Car());
wheeledAndColouredVehicles.add(new Bike());
WheeledAndColoured first = wheeledAndColouredVehicles.get(0);
String firstWheels = first.getWheels());
String firstColour = first.getBodyColour());
If you really want to, you can extract the wheels field into a super class, but the immediate gain is limited. Private fields are an implementation detail, and pulling them into a super class is certainly not the type of re-use that will make a big difference. At the same time, it would create a very strong dependency between those classes and make future changes harder.
Maybe if you need to implement some 'complex' shared logic related to wheels, it is time to create the Wheels class:
class Wheels {
private String type;
private int number;
private BigDecimal price;
public BigDecimal getReplacementCosts() {
return price.multiply(BigDecimal.valueOf(number));
}
}
Now the Car class can collaborate with the Wheels class as one of its fields:
class Car implements Wheeled {
private Wheels wheels;
#Override
public String getWheels() {
return wheels.getType();
}
public BigDecimal getMaintenanceCosts() {
return wheels.getReplacementCosts();
}
}
Notice that, because we didn't tie in Car and Bike with a common super class, we are not forced to change either the Wheeled interface, nor the Bike class. If you want to add the Wheels logic to Bike then you can easily do so, but you are not forced to. You would be, if the wheels fields was in the shared super class Vehicle.
So the motto is: Favor collaboration over extension because it's way more flexible.
I agree with other commenters that the Vehicle class does not really make sense. I recommend following their advice and having Car and Bike extend the Vehicle class. This is better than using generics, because you can "guarantee" that you cannot use AssembleVehicle with classes that aren't vehicles and don't meet the requirements of having a wheel and a bodyColour.
However, if you still want to use generics, here is how you can use them:
class Car {
String wheel;
String bodyColor;
//setter & getter
}
class Bike {
String wheel;
String bodyColor;
//setter & getter
}
class AssembleVehicle <T> {
public void init(T vehicle) {
getDetails(vehicle);
}
private void getDetails(T vehicle) {
String wheel = vehicle.wheel;
String bodyColor = vehicle.bodyColor;
}
}
This allows you to write the getDetails method only once.

Using variables for creating objects and calling methods in Java

Hi Everyone I am beginner in java and came across a question like Can I use variables for creating objects and calling methods to reuse the code.
Tesla.java
public class Tesla extends Car {
#Override
public void buy(){
System.out.println("Tesla bought");
}
#Override
public void sell(){
System.out.println("Tesla Sold");
}
}
Ford.java
public class Ford extends Car {
#Override
public void buy(){
System.out.println("Ford bought");
}
#Override
public void sell(){
System.out.println("Ford Sold");
}
}
Car.java
public class Car {
public static void main(String[] args) {
String[][] arr = {{"Tesla, Buy"},{"Ford", "Sell"},{"Benz", "Sell"}};
Car car = new Tesla();
car.buy();
Car car = new Ford();
car.sell();
}
public void buy() {
System.out.println("Car bought");
}
public void sell() {
System.out.println("Car Sold");
}
}
Here instead of creating each object I just want to use one for loop and create respective object and respective method based on the array elements.
Logic like below.
public static void main(String[] args) {
String[][] arr = {{"Tesla, Buy"},{"Ford", "Sell"},{"Benz", "Sell"}};
for(int i = 0;i<arr.length-1;i++){
Car car = new arr[i][0]();
car.arr[i][1];
}
}
How to achieve above logic? Is this something achievable in Java? I searched in google couldn't find relevant questions or problems. Please help me. Thanks in advance.
Note:- I don't want a workaround I just want to know the if logic is achievable using any advanced java concepts I am unaware of.
If you want to instantiate objects of various subclasses according to string inputs, you have at least two options:
Reflection
Builder pattern
Reflection
As commented by Nikolaus, one route is to use Java’s reflection facility. This is the “magic” way, where you would find at runtime the name of the class matching your string input. For example, "Tesla" string would lead you to loading an object of type Class representing the Tesla class you wrote at compile time. You would call methods on that Class object to create an instance of your subclass. In other words, you are programmatically doing a roundabout replacement for the code new Tesla(…).
I do not recommend going the reflection route. This is not “normal” Java app programming. Reflection is usually done only in certain kinds of frameworks and in special rare circumstances.
Builder pattern
The other route more commonly used is the Builder pattern. You define another class called something like CarBuilder. You pass your text values into one or more methods of an object of this type CarBuilder. Those methods validate the inputs.
When done setting up the various pieces of input, you eventually call a method conventionally called build. That method produces and returns an object of type Car. That Car object is actually from a subclass, is actually a Tesla or Ford.
CarBuilder builder = new CarBuilder() ;
builder.setBrand( "Tesla" ) ;
builder.set… = … ;
…
Car car = builder.build() ; // Actually a `Tesla` subclass object.
Rather than create different classes (Tesla, Ford) that inherit from a superclass (Car), just pass in parameters to your Car class methods:
public class Car {
public void Buy(String brand) {
System.out.println(brand+" bought");
}
public void Sell(String brand) {
System.out.println(brand+" Sold");
}
}
I would also move the main method out to a separate Runner class. Its only responsibility would be to run the program and nothing else. That way you decouple the classes that implement your model (Car...) from the classes used to run the program.
Additionally, my example is a bit weak in the sense that I have to pass in the brand for each method. What you should do instead is introduce the notion of a constructor in your Car class and the notion of a class attribute. Your code then becomes
public Class Car{
private String brand;
public Car(String brand){
this.brand = brand;
}
public void Buy(String brand) {
System.out.println(brand+" bought");
}
public void Sell(String brand) {
System.out.println(brand+" Sold");
}
}
One last thing: methods typically don't start with a capital letter in Java so you should rename Buy and Sell to buy and sell.
Your second requirement is also to parameterize the action (buy or sell). You can apply the same principle i.e. have a generic method (doAction()) that will now take in 2 parameters: the car brand and the action you want to do. But IMHO that's pushing it too far and losing value.

Composition over Inheritance and Tight Coupling

I am a complete beginner so forgive my ignorance.
I have created a project where I have used composition in some classes.
In my Cinema class I have a Schedule object.
public class Cinema {
private String name; //set via constructor
private int seatCount; // set in constructor
private int rowCount; // set in constructor
private int cleanUpTime; //set via constructor
private LocalTime openTime = LocalTime.of(9, 30);
private LocalTime closeTime = LocalTime.of(23, 59);
private LocalTime peakTime = LocalTime.of(16, 30);
private int costPerHour; //set via constructor
private Schedule schedule = new Schedule(this);
//Constructors, other methods....
}
A Schedule belongs to a Cinema. It needs a Cinema object for some of its methods. A Schedule can not exist without a Cinema.
When reading about OOP I am led to believe that I have created a class that is now tightly coupled to another class and that is potentially bad.
Therefore how could I improve this design?
I have a few tightly coupled classes it seems. e.g Booking class and Customer class. A booking has a Customer and a Customer contains a list of all Bookings they have made.
I thought I was using composition and that would be good but now I am confused as I have read about coupling.
Please help me understand.
There has to be some coupling. A Cinema and a Schedule are not completely independent.
A Schedule belongs to a Cinema.
So far, so good.
It needs a Cinema object for some of its methods.
Nope. A Schedule object should be able to stand on it's own.
Since you haven't provided any code, I'll make the following assumptions.
A cinema shows one or more movies.
A movie has a schedule for each day of the week, for as long as the movie is shown.
So here's a Schedule class.
public class Schedule {
private final Calendar showingTimestamp;
public Schedule(Calendar showingTimestamp) {
this.showingTimestamp = showingTimestamp;
}
public Calendar getShowingTimestamp() {
return showingTimestamp;
}
public int getShowingWeekday() {
return showingTimestamp.get(Calendar.DAY_OF_WEEK);
}
}
The only field on the Schedule class holds a showing date and a showing time. I showed you how to use a Calendar method to get the weekday.
Here's a bare bones Movie class.
public class Movie {
private final String name;
private List<Schedule> showingList;
public Movie(String name) {
this.name = name;
this.showingList = new ArrayList<>();
}
public void addShowing(Schedule schedule) {
this.showingList.add(schedule);
}
public List<Schedule> getShowingList() {
return Collections.unmodifiableList(showingList);
}
public String getName() {
return name;
}
}
The Movie class knows about the Schedule class. The Schedule class does not know about the Movie class.
Finally, here's the Cinema class.
public class Cinema {
private final String name;
private List<Movie> currentMovieList;
public Cinema(String name) {
this.name = name;
this.currentMovieList = new ArrayList<>();
}
public void addCurrentMovie0(Movie movie) {
this.currentMovieList.add(movie);
}
public void removeMovie(Movie oldMovie) {
for (int index = currentMovieList.size() - 1; index >= 0; index--) {
Movie movie = currentMovieList.get(index);
if (movie.getName().equals(oldMovie.getName())) {
currentMovieList.remove(index);
}
}
}
public List<Movie> getCurrrentMovieList() {
return Collections.unmodifiableList(currentMovieList);
}
public String getName() {
return name;
}
}
The Cinema class knows about the Movie class, and indirectly, about the Schedule class. The Movie class does not know about the Cinema class.
I hope this has been helpful.
It is fine to have a Cinema object and list of Schedule objects inside it. One to Many relationship I suppose.
Where suitable, using composition instead of inheritance is indeed a good design practice. Your question is not really about that, though: I don't see a plausible inheritance-based alternative to the composition you have formed.
Suppose that you were trying to design classes for a system that can distinguish IMAX cinemas from other cinemas, with a consistent interface but different behavior. You might consider creating ImaxCinema as a subclass of Cinema and overriding methods as needed to customize its behavior:
class ImaxCinema extends Cinema {
#Override
int getScreenWidth() {
// ...
}
}
That's the the "inheritance" alternative.
On the other hand, you might create an interface ProjectorType, with implementations Standard and Imax that implement the varying behavior. If you give the Cinema class a member of type ProjectorType then you can provide for the varying behavior by the class of the object assigned to that member:
class Cinema {
ProjectorType projector;
int getScreenWidth() {
return projector.getScreenWidth();
}
}
That's a common form of the "composition" alternative.
Your situation does not bear on inheritance vs. composition because no customization of behavior is involved.
Avoiding tight coupling between classes is a separate consideration, and also a good design principle. Your Cinema and Schedule classes are indeed tightly coupled, as is evident already in the fact the Schedule's constructor requires a Cinema argument.
Consider, however, the iterators of Collections classes. Each one is inherently specific to a particular collection class, as it must navigate the idiosynchratic internal data structures of that class to do its job properly. Each iterator's class is therefore tightly coupled to the associated collection class, and that's OK. That avoiding tight coupling is generally a good principle does not mean that the quality of every single design is anticorrelated with the degree of coupling.
In your particular case, I don't quite see what advantage you get from the Schedule class, nor either why it needs to be tightly coupled to Cinema. Possibly, you can break the coupling, maybe by moving members of Cinema into the Schedule class, and having Cinema access them by invoking appropriate methods of Schedule. Alternatively, it might make more sense to just merge Schedule into Cinema instead of having it as a separate class. If neither of those is viable, then you could consider going all the way and making Schedule an inner class of Cinema.
Good sense, indeed tight coupling. Prevent it here by creating a new Interface, making Schedule more reusable probably. In the interface place all methods you want to use from Cinema. The IDE's compiler will help you there. Do not forget to add #Overridable in Cinema, for those interface methods.
public class Cinema implements Schedulable {
private final Schedule schedule = new Schedule(this);
public class Schedule {
public void Schedule(Schedulable schedulable) { // Instead of Cinema

Intuition vs Design principles

I have a class hierarchy like this Beverage -> Coffee-> Latte.
Where Beverage is the abstract superclass being extended by Coffee. Coffee class then adds some behavior but is also abstract. Latte extends Coffee class and is a concrete class. I have used inheritance to add behaviors here. And inheritance do have drawbacks like the visibility of superclass methods, making code fragile, the code is tightly coupled. So, programming principles dictate Composition should be preferred over Inheritance. But in this case inheritance feels so natural as Latte is a type of Coffee and Coffee is a type of Beverage that using composition to add behavior feels wrong in spite of its benefits. So the question here is
Should Intuition override Design principles?
Beverage:
public abstract class Beverage {
private final String name;
private final double price;
Beverage(String name, double price){
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public abstract void make();
}
Coffee:
public abstract class Coffee extends Beverage {
public Coffee(String name, double price) {
super(name, price);
}
public final void make(){
grindBeans();
takeShot();
frothMilk();
addCondiments();
}
public void grindBeans(){
System.out.println("Grinding Beans...");
}
public void takeShot(){
System.out.println("Taking Shot....");
}
public abstract void frothMilk();
public abstract void addCondiments();
}
Latte:
public class Latte extends Coffee {
public Latte() {
super("Latte", 4.0);
}
#Override
public void frothMilk() {
System.out.println("Frothing milk to create micro foam");
}
#Override
public void addCondiments() {
// TODO Auto-generated method stub
}
}
EDIT: Adding Sugar to existing structure. Only new code is shown.
public abstract class Beverage {
private Sugar sugar;
public Sugar getSugar() {
return sugar;
}
public void setSugar(Sugar sugar) {
this.sugar = sugar;
}
}
Coffee:
public abstract class Coffee extends Beverage {
public final void make(){
grindBeans();
takeShot();
frothMilk();
addSugar();
addCondiments();
}
public void addSugar(){
Sugar sugar = super.getSugar();
if(!(sugar instanceof NoSugar)){
System.out.println("adding " + sugar.getTeaspoon() + " teaspoon sugar");
}
}
While composition has many benefits over inheritance, there's nothing wrong with using inheritance where it feels natural (i.e. in a truly is-a relationship). If it's natural, go ahead and use it.
Composition means A has a B, and inheritance means A is kind of B. In your case you're 100% right - inheritance should be used: latee is a cofee and a cofee is a beverage.
Why do you consider it to be fragile? E.g., latee should have all the properties of cofee, but can implement them differently. Nothing fragile here - it's a polymorphism. If you want to restrict overriding of parent's methods - mark them final.
As an example of composition - there are Car and Wheel. Car has a wheel.
Car needs wheels for working, but they're completely different objects. Car can be opened, closed, started, etc.. - wheel can't.
Wheel can revolute and deflate. Car - can't.
P.S.:
Oh, I think I got what you mean by "fragile". Here is an article on it http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html
For me this "composition instead of inheritance" thing still looks like an OOP hack (especially, the example in the article: apple IS A fruit, no way around it :))
Whenever I will see this hack in use, I would probably think of an API designer to be improvident.
I think in the book head first design patterns the example they use is that the latte class can have several configurations. By using composition you can create custom classes at runtime rather than during design
You can only extend from one class as well which may be a limiting factor depending on what you are doing.
Yes, at first sight inheritance here seems all right going with the argument that wherever its naturally so it makes sense to have inheritance. Coffee is a beverage and latte is a coffee so inheritance is okay.
Though it makes sense, what if we have more types of coffees - cappuccino, frappe etc. Then do we make more sub-types? I think then we can use a decorator pattern where Latte extends coffee and will itself be a coffee and so on. In this case we will make use of composition and inheritance both together. So, twisting the argument a bit, in this case we need both Composition and Inheritance and not either/or.
Having said that( and if you bought what I said above), what if we keep on decorating this Latte say by having variants American Latte and French Latte. Then which is our base class for a decorator? Is it Coffee or Latte? If we choose Coffee then American Latte is composed of Latte which is composed of Coffee. Composition is pulling it all together. If, however, we say its Latte then we make another base decorator component i.e. Latte. Then we have 2 levels of decorators - one centered around coffee and another (1 level below) centered around Latte which makes it too confusing. So, it makes more sense to have Coffee as the abstract decorator and use composition to bring in Latte and then French/American latte. Then, composition has an edge over inheritance here.
If you are developing simple system where you have four subtypes of coffee then you should stay with inheritance.
Solution with inheritance have its problems. They will be visible especially when amount of coffee subtypes will increase.
First problem is with unimplemented methods. You will most likely have some methods which will just have empty implementation.
public class Espresso extends Coffee {
public Espresso() {
super("Espresso", 4.0);
}
#Override
public void frothMilk() {
// well I don't really need that method, so I will just write a comment
}
#Override
public void addCondiments() {
// that one is also unnecessary, does it mean that my inheritance tree is wrong?
}
}
Second problem will occur with exponential explosion of parameters in your coffees. If you decide to have Latte with sugar you might end up with special class for this Latte, LatteWithSugar, which will also create new *.WithSugar for each already existing class. The same with each special modification of class. Please note that amount of classes increases exponentially. If you have 8 kinds of coffee adding one parameter will make 16 classes which is suspicious.
It is a matter of scale.
For small app it might be overengineering. For big coffee shop application it will save you hours of maintenance.

How do I refactor my model?

I have object model, which describes an instructions for worker:
go to location
get an item
go to another location
pick another item
........
finish job
In my app I need a list of this instructions to guide worker in his job.
The types of instructions are very different, that's why data which is required to describe instruction can be different. For changing location I just need the location id, but for getting item I also need an item id, quantity and item name, alongside with location id.
So my model looks like that:
public class Instruction{
int locationId;
int itemId;
String itemName;
String quanity;
Type itemType; // can be Type.GET_ITEM, Type.CHANGE_LOCATION etc.
}
So I have the situation where I describe different entities with one model.
Should I live with that, or there's a way to eliminate duplication?
What I want is to keep the ability to store all instructions in a list, but to make models more clean.
How about something like this?
interface Instruction {}
class LocationInstruction implements Instruction {
int locationId;
public String toString() { return "Go to the location " + locationId; } ;
}
class ItemInstruction implements Instruction {
Item item;
public String toString() { return "Pick item " + item.toString(); } ;
}
class Item {
int itemId;
...
public String toString() { return "Item{itemId: "+itemId+"}" ;}
}
class Actions {
public List<Instruction> getInstructions();
}
Extract common information into an abstract class and create classes for specific types of instructions that extend the abstract class.
Your abstract class could offer some common methods for common operations and/or define methods the subclasses need to implement.
That can give you a clean interface to work with in your lists.
Take a look at polymorphism in Java.

Categories

Resources