I am learning software design now. I am a front-end guy so this may be a stupid question, but I really want to know the answer. Hope you could help me.
I have to design an E-commerce system. More specifically, I am designing the buying system in E-commerce. I have done some research on how to do it and found EAV. But after knowing the EAV is the anti-pattern, I don't want to use it and also I have to keep it simple for a beginner like me to understand the design.
Here is the class diagram I have designed by myself.
And of course, I don't think this design is correct. I have spent like three days doing research and thinking about how to solve the Product and ProductType problem.
I just want to have a product like an iPhone, for example, has the attributes belong to the phone, a coke has the attributes belong to the drink, etc.
How could I do this?
Please tell me how to solve this problem in a simple way, cause I am new to this. Some articles or books about software design could be appreciated too.
Thank you <3
Basically you know that all the products have (at least) a product type. And you know that a product type instance will end up being a drink, a telephone, etc. So, you first need
Product Type Abstractization
You will need to make sure that ProductType is either an interface or an abstract class or a base class. An interface is a declared entity type, whose capabilities are known, but not implemented. It is the job of the classes implementing the interface to implement its methods. An abstract class is a fully declared, but only partially defined entity type. If you have an abstract class, then you are able to implement some of its methods, but you delegate the implementation of some of its method to its implementing subclasses. A base class is a class which is fully defined.
So your first decision is to make ProductType one of the following:
interface
abstract class
base class
You will need to think about what the common capabilities of product types are. If they should have some methods which work exactly the same, then you do not necessarily need an interface, but you will need an abstract class or a base class. If you decide not to define an interface at this point, that's fine. Later you can define it if you realize that you need it anyway. So, assuming that the methods of the separate product types are at least partially common, you will need to have some class. By default it should be a base class, that is, a normal class which has all the methods a ProductType should have implemented. Son't worry, if some specific product types should behave in a different manner in the case of some methods in comparison to the base class, you can always override base class methods for subclasses.
However, you might need an abstract class. In order to decide whether an abstract class is the way to go is to find out whether there is at least such a method that should NOT be implemented by the base class in any circumstances, because that method is always known only on subclass-level. For example, if you have an evaluate method, then you will probably need to implement it separately for your product types, because a phone is evaluated in a different manner in comparison to car.
Next, you need to define specific ProductType subclasses, that is, classes which extends/implement ProductType. We know that a ProductType may have 0 or more Products, but can a product be of more product types?
Handling one-to-many vs. many-to-many relations
A product will need to have a ProductType if there is no possibility for more product types to be associated to a single Product. Otherwise you will need a collection of product types by product.
Abstractization of Product
Since Product is also something much more general, you will probably need to invoke Product methods from ProductType. This means that you will need to decide whether Product is an:
interface
abstract class
base class
as well, with a similar thought process as the one you have used when decided what ProductType should be.
ProductType and Product trees
It's a big question whether there can be subtypes, sub-subtypes, etc. for ProductType and Product. If that's the case, you will need to implement trees for them with proper insert/update/delete/search functionalities, as you need them
Use your abstractizations
Whenever possible, do not refer to specific product types, because then you will have to copy-paste that code for other products and product types. Whenever the same pattern is true for all your product types or products, refer them by their most abstract representation (their interface, abstract class or base class, respectively) and only use concrete types at instantiation and when you are forced to.
Factories
Use factory methods for instantiation instead of constructors, because a factory method can return the instance of a subtype if that's what you need.
Related
My design problem is as follows.
I have two classes, each with a number of subclasses. I have a factory, which needs to create an object based on the subclass of each of these objects.
This is an authentication problem. The factory generates a rule object based on the type of person and the type of resource they wish to access. The rule has alwaysAllow, NeverAllow and timeBasedAllow subclasses. With the potential for more if a more complex access system is needed in the future.
So in future ideally a new person could be created with a new subclass, a new resource with a new subclass. The parameters on which access is determined could be changed with a new rule subclass, and the specific access of each person type and room type could be changed within the rule factory.
So far the only way I can think to do this would be to have an enumeration inside the subclasses, which defeats the point because then adding a new person or room requires a new class and a change in the enum class which seems messy.
I also am very keen to keep the data and the logic separate so I can’t just move authentication methods into the person class because this would require the person class to know how many room types there were, which is definitely not ideal.
I may be after something that isn’t realistically achievable but I can’t help the feeling that there is a nice clean solution just out of my grasp.
Any help would be greatly appreciated.
Your question title makes it sound as if you are searching for multiple-inheiritance, which is not allowed in Java. Unlike in C++, a class may extend one and only one class. However, Java also has interface, which I suspect may be what you seek.
An interface class cannot be instantiated, and may have abstract methods. A
concrete class may implement as many interfaces as desired, and the concrete class must implement each abstract method the interface declares. Abstract classes may also implement interfaces, and abstract methods they do not implement must be implemented by concrete classes extending them.
I suggest extracting your authentication methods into aninterface, perhaps called AuthRule or somesuch. AuthRule can have abstract methods with represent authenticating, without exposing the exact style used to authenticate. So, you would implement AlwaysAllow implements AuthRule and then the authenticate methods on AlwaysAllow would always return true.
The second thing, however, is that you appear to be attempting to use inheritance when composition would better suit your needs. Now instread of having a Person inherit his authentication-rule, the rule should instead be a member field inside Person. So, for example:
class Person extends User {
AuthRule rule;
Person(AuthRule myrule) {
rule = myrule;
}
bool authenticate(...) {
return rule.authenticate(...);
}
}
If you follow a design pattern based on injecting objects into other objects to mix in the functionality you desire, your code will become far more usable and extensible. I hope this helps your problem.
I have the main abstract class that is a base for bunch of classes. Some of them does not need all the fields and methods from the main abstract class, so I have created second abstract class and splitted main abstract class into two parts. The main abstract class contains, for example, a, x fields and their getters/setters, the second abstract class inherits from the main and contains additional b, c fields and their getter/setters. There are simple classes that are inheriting from the main class,and more complicated are inheriting from the second class. I want to create objects of each class as instances of the main class. Is it right way to do that? I have to type check and cast when I want to use methods from the second abstract class. It makes my code complicated. How can I solve this problem?
MainAbstractClass ---> SecondAbstractClass ---> MyComplicatedClasses
|
|
V
MySimpleClasses
One of the OO principles is Favor composition over inheritance.
This means that common behavior is not provided through base classes but via Component classes which are passed in via dependency injection (preferably as constructor parameters.
The answer depends on your actual needs.
You can instead choose to store the extended abstract class specific fields in a class that does not implement your base class and make it a member of more complicated classes.
You can choose to keep everything in a single base class and nothing forces you to use all the fields of an interface in every class that implemented your interface.
You can also keep using your approach but since you store the classes as an instance of the base class, it will be hard to read.
I believe that if you think code does not look very good, it is probably not good. However, there is usually no single answer to this kind of design questions and the best solution is relative to your preferences.
I think this need of type cast is a smell of fragile design. Here when we assume MyComplicatedClass ISA KIND OF MainAbstractClass as shown by TJ Crowder then object must behave as MainAbstractClass (meaning it can honor only API of MainAbstractClass). If it expects special treatment as MyComplicatedClass its false commitment and will need Casting. Such casting (by identifying type) goes against OO principles and kills polymorphism. Later this will end up in Ladder of InstanceOf and type casts as in the scenarios rightly pointed out by T.J. Crowder.
I would suggest readdress the design. e.g. though our all user defined type instances ARE KIND OF Object, but we use Object API only for methods defined in Object class. We do not use Object o = new MyClass(). There are occasions in frameworks or like Object.equals() method where type cast is needed as API is defined before even concrete extension is written. But it is not a good idea for such simple complete (without open hooks for extensions) Hierarchies.
I would also like to quote from a source from where i read and could not understand.
We all know by default, all primitive datatypes (int, float etc) support basic operations such as addition and subraction. The system provides the implementation for the primitive datatypes. For userdefined types we need to define operations. The implementation of these algorithms is done when we use them.
That means, user defined datatypes are defined along with their operations.
To simplify the process of solving problems, we combine dataStructures along with their operations and call it as AbstractDataType.
Can anyone explain a good example of it and to the context of real world too?
Why u need them?
Source Narasimha Karumanchi book DataStructures and Algorithms
Good question!
In terms of application infrastructure and design, abstract data types are useful in order to have a clean and understandable hierarchy of classes and data models.
As you asked for a practical example, take any application that uses a database: say an online eshop.
Primitive data types are useful, of course, say we use doubles to store prices, ints to store the number of items and strings to display and store item names / descriptions, but how do we deal with the basket/cart? Easiest and most recommended way is to create an abstract representation of the cart via a separate class, say shoppingCart, which will contain its properties:
class shoppingCart{
int numOfItems;
double totalPrice;
List<Product> products;
etc.
}
Also, we would need classes to represent the Products and categories such as:
class Product{
double price;
string name;
etc.
}
Think of any abstract data type that you create as your own objects, with their own methods and so on.
We would use abstract classes to define general behavior of certain subclasses that extend them, such as: abstract class Vehicle and classes Car, Bike that extend Vehicle.
The structure would be:
abstract class vehicle{
public void drive();
public int getNumberOfWheels();
}
This way we make sure that all instances of Car and Bike will implement the 2 methods listed in the abstract class vehicle:
class car extends vehicle{
//we have to implement the 2 methods and provide an implementation for it
}
Another developer would like to create a new type of vehicle (say "plane"), so he could now see that he has to implement the 2 methods no matter what.
I hope I could help you.
Assume, we have an abstract class A and we want to force all subclasses to have a certain field. This is not possible in Java, because we can not define abstract fields.
Workaround 1: Force subclasses to implement a method which delivers the wanted value.
abstract class A {
abstract int getA();
}
Drawback: Each subclass has to implement a method for each abstract field we want to have. This can lead to many method implementations.
Advantage: We can use the method getA in the abstract class and implement methods with it in A without implementing them in each subclass. But the value behind the method can not be overwritten by the abstract class.
Workaround 2: Simulate the abstract field by forcing the subclass to give the abstract class a value.
abstract class A {
int a;
public A(int a) {
this.a = a;
}
}
Drawback: When we have multiple fields (> 10), the super constructor call will look a bit ugly and confusing.
Advantage: We can use the field a in the abstract class and implement methods with it in A without implementing them in each subclass. Plus, the value a can be overwritten by the abstract class.
Question: Which workaround is the common way to reach the goal ? Maybe there is a better one than the above ones ?
The abstract method is probably the most object oriented.
If you have too many fields, you may want to regroup those in a POJO (if a new concept is appropriate).
I prefer the first one . i dont love to couple classes in fileds name , how they handle state and how they save it. the first one is more close to open/close principal
I recommend to avoid inheritance. inheritance is very frigle and hard to maintenance. remember effective java advice - prefer composition other inheritance
I think opt.1 is the cleaner by far. A few getters and setters is not a big deal, and I doubt that many use cases would have more than just a few abstract "fields".
About opt.2, you forget that constructors are not inherited, and thus would require all sub classes constructors to be implemented in a way that takes a into account.
Workaround 2 is very common because of 2 advantages:
1) the one you mentioned - the field does not belong to the subclass - it belongs to the parent and that is important because it was "demanded" by the parent and because the parent can use it
2) When sub-classing from the parent you are very aware of this field because when you implement the constructor you must pass it on. If I saw the first workaround I wouldn't know what to understand from it, in this way I understand that the parent class needs this field to work, so it must have a meaningful value.
note: if you have a class that has 10 fields that need to be initialized something is probably wrong in your design.
1. Actually its not about what one prefers but its about the flexibility, and the ability
to adapt changes.
2. Its always better to Encapsulate Behaviors that keeps changing, either into an Interface or Abstract class.
3. You 1st Workaround will be good in places where you need different implementation for the same Behavior in Different classes. Then at this place either an Interface or your 1st Workaround will be a good choice.
Eg:
Consider Painting as a Class with paint() Method.
Now
paint() method can have Stroking, gliding, shading etc styles of doing it.
Then its better to Encapsulate that method into an Abstract class or an Interface.
public interface Paint{
paintDoIt(String style);
}
4. Your 2nd Wordaround will be good in a place, where you want certain behaviors to be MUST implemented by the Subclass.
Eg:
Consider Car as an Abstract Class, Now to be car its very important that it must have
a Steering, 4 wheels, Engine, etc. So these features must be implemented.
where as other features like music system, LCD ,etc are optional and depends on the car type.
After all ANY java abstract is an abstract subclass of Object. Sometimes we need to force the subclass to implement some methods, but may already have a pretty well defined hierarchy with concrete classes.
For example: I have a well functioning hierarchy with
Vehicle<--- Car
and now I want to add ElectricCar to this hierarchy.
vehicle<--Car<--ElectricCar.
I also want all the different types of electric cars to implement certain behaviors like getBatteryLife or something-
Why would it be a bad idea to make ElectricCar abstract ?
there's nothing wrong in making it abstract. if your business requires you to make it abstract, it's fine. Like you said, lots of classes in Java lib are abstract and still extending Object.
It's not bad, per se. Not common, but not bad. The only thing I can think of is understandability: if I saw a concrete class Car that I could instantiate, I would normally assume that any child of it was also instantiable, because 99% of code works this way.Then I'd be confused, for a second, about not being able to instantiate an ElectricCar.
It could be argued that this pattern breaks the Liskov Substituion Principle since you can't pass "ElectricCar" wherever "Car" is expected if it's declared abstract (you could pass instances of ElectricCar subclasses of course).
In this particular example, the concrete electric cars (hydrogen powered/plug-in/etc?) I would expect to inherit directly from "Car" since they satisfy an "is-a" relationship and are a proper specialisation of "Car". If you wanted to described some common behaviours and traits they should provide then they should also implement an ElectricCar interface.
It seems what you really want is the ability to inherit from Car (since that is what they are) and share/re-use common implementations of electric car related methods. In this case you are looking at a multiple inheritance problem or a need for mixins, neither of which are directly supported in Java.
Providing an abstract class in the middle of a concrete hierarchy may be one way around this, but it's not pretty.
Personally I would prefer to define an Interface for ElectricCar and then allow the implementing class to define the methods. Then you can share the behavior of getBatteryLife through another mechanism.
I've built some pretty deep hierarchies of Inheritance and I tend to avoid them do to the brittle nature they tend to build up over time. One Base class might make sense, but I would think about how you can compose your object model to share behavior w/o inheritance if possible.
In you example I would say that supposedly the Car class should be abstract (or an interface), too. But there is nothing wrong with the ElectricCar being abstract.