Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
I was going through some object oriented design problems for learning and I came across this problem where a Book object is needed in a book catalog. This is the Book object that is suggested in the solution to the problem.
public class Book {
private long ID;
private String details;
private static Set<Book> books;
public Book(long iD, String details) { ... }
public static void addBook(long iD, String details){
books.add(new Book(iD, details));
}
public void update() { }
public static void delete(Book b) { books.remove(b); }
public static Book find(long id){
for (Book b : books)
if(b.getID() == id) return b;
return null;
}
}
In one way this Book object looked quite good to me since it contains all the methods that are needed to modify/get info about the book object as well as the data about the book. So when going with the definition of OOP this looks great since that is what an object should be.
But the way I had been doing stuff in my 1-2 years of programming career I always thought that creating, deleting. modifying an object should be done via a service layer, essentially a BookService class in this case which contains methods to create Books, update books and delete books from the database using the Book object which doesn't contain these CRUD methods.
The first approach looks great in theory while the next one is great in practice as I know from whatever experience I have. Are there flaws/pitfalls of the second approach? Which approach should be preferred ?
PS: I am not sure if such questions are accepted and I would gladly delete/edit it if they aren't but I don't find a better place to get the answer :(
Your Book object is a so-called "domain object". Its only responsibility is to provide the so-called "business logic". E.g: it has a state, defined by the class members, and it can contain class methods (for calculations, etc) to interact with the state. No other objects should know about the internal business logic implementation.
Now, the domain objects are also known as "models". But that can be a bit confusing. Why? Because a "model" is actually a layer. And it is composed of three sublayers:
The domain layer (domain model), consisting of the domain
objects. Through their structure and interdependence with each other,
they are the abstraction of the real world (business)
objects/entities. This layer could also contain structures like
collections of domain objects.
The storage layer, composed from the classes responsible with the
transfer of the domain objects into/from the underlying storage
system (may it be RDBMS, file system etc): repositories, (data)
mappers, adapters, data-access abstraction classes, etc. The
use of these structures also achieve the purpose of making the domain
objects (completely) agnostic, unaware of the storage type and the
way in which it is addressed.
The service layer is built from classes (e.g. services) that
execute operations involving the structures from the upper two
sublayers. The user interaction with the program should take place
only through services.
So, in your case, the domain object Book would look like this:
public class Book {
private long ID;
private String details;
public Book() { ... }
// Setters/getters...
}
Then you'd have a data mapper (BookDataMapper) as well:
public class BookDataMapper {
private DbAdapter adapter;
private Set<Book> books;
public BookDataMapper(DbAdapter adapter) {
// Assign DbAdapter object to the adapter class member.
}
public void select(long bookId) {
// 1. Fetch book record from db by bookId and using the injected db adapter.
// 2. Map fetched db record to a Book object using mapBook().
// 3. Add Book object to books using addBook().
}
public void insert(Book book) {
// 1. Read the class members of object book.
// 2. Inject the values in an INSERT SQL statement as parameters.
// 3. Run the INSERT query and return last insert id.
// 4. Assign the last insert id to book's ID class member.
// 4. Return book.
}
public void update(Book book) { ... }
public void delete(Book book) { ... }
public void mapBook(array bookRecord){
// 1. Create a plain Book object.
// 2. Read bookRecord array and map each field to the corresponding
// class member of the Book object.
// 3. Return mapped Book object.
}
public void addBook(Book book){
// Add book to books collection.
}
}
You could define a higher layer of abstraction for the data access as well, e.g. a BookRepository. You can/should also move the book collection (code) inside it:
public class BookRepository {
private BookDataMapper bookMapper;
public BookRepository(BookDataMapper bookMapper) {
// Assign BookDataMapper object to the bookMapper class member.
}
public void find(long bookId) {
// 1. Use bookMapper to fetch book record from the storage by bookId.
// Notice that I said storage, not db: per definition, a repository
// hides the details regarding the storage type. The user (client)
// knows only that the book is placed... somewhere.
// 2. Return the fetched book object.
}
public void store(Book book) {
// 1. Use bookMapper to store the book.
// 2. Return the book (with last insert id in it).
}
public void update(Book book) { ... }
public void remove(Book book) { ... }
}
And at last, define a service (BookBorrowingService) to manage the book borrowing process when a user wants to borrow a book from the library:
public class BookBorrowingService {
private UserCardRepository userCardRepository;
private BookRepository bookRepository;
public BookBorrowingService(UserCardRepository userCardRepository, BookRepository bookRepository) {
// 1. Assign UserCardRepository object to the userCardRepository class member.
// 2. Assign BookRepository object to the bookRepository class member.
}
public void borrowBook(long userCardId, long bookId) {
// 1. Use userCardRepository and the given card id to find the user card.
// 2. Validate the card based on its details. If successfull go further.
// If not, then return corresonding response to user.
// 3. Use bookRepository and the given book id to find the book.
// 4. Return the fetched book object.
}
}
Then, in the main part bind all pieces together:
// Create and share db connection(s).
// Create and share adapter(s).
// Create mappers.
// Create repositories.
BookBorrowingService bookBorrowingService = new BookBorrowingService(userCardRepository, bookRepository);
Book book = borrowBook(123, 4567890);
To answer to your question(s):
The advantage of structuring your code in this way is, that each class has very good delimited responsibilities, conforming to the Single Responsibility Principle. For example, a domain object's responsibility should only be business logic, not data access. So, in short, this SOLID principle contradicts your first approach. You can also say that a separation of concerns take place.
The only disadvantage of using the described approach is only that you'll have to write more code.
Notes:
I don't program in Java. That's why I didn't implemented more code.
Use a dependency injection container.
Forget about statics, globals, singletons.
Use interfaces instead of concrete implementations.
Good luck!
In case you are implementing a console application with study purpose, it`s not a big deal if you implement CRUD logic into the model..but i do not think that this is the case.
This model Book which you have implemented must have only object properties plus getter and setters. The other CRUD methods which you implemented must be in a external layer. A external layer may be a SERVICE or a DAO, it depends.. But you have to know that it is not a good practice if you write some extra logic in model classes like now.
A person has an ID.
A student has a student's ID.
A driver has a driver's license.
A person attends a school and becomes a student.
A student graduates and becomes an ordinary person.
A person gets a driver's license and becomes a driver.
A student gets a driver's license and becomes a driver.
Are those things just state change like in the following example:
class Person {
ID id;
StudentID stId;
DriverLicense license;
void drive() {
if(license == null) //illegal state exception
//drive
}
//bla bla
}
Or is there inheritance? Since an object is data+behavior, new data and being able to do new things should warrant a new object
class Student extends Person {
//
}
class Driver extends Person {
//
}
//things get messy here, in Java you can't extend multiple class
//what if there's a rule that, student drivers can request/get a tax reduction?
class DriverStudent extends Person, Driver {
//
}
PLUS, more importantly, how does one become the other? Through methods or constructors or 3rd classes(like a service or aggregate) ?
class Person {
Driver getADriversLicense() {
//create and return a Driver
//this person still exists but now there's a driver with this person's data
}
}
or:
class Driver extends Person {
public Driver(Person p) {
//constructor
}
}
or:
class Aggregate {
Driver giveDriversLicense(Person p) {
// access internal state of both objects(ditch encapsulation) and return a driver?
// put aggregate in same package with Driver and Person and use package private methods to provide encapsulation?
}
}
A better way to see such relationship is through roles i.e. a Person can play multiple roles .
How about a scenario where a Person can be a Driver and a Student and may be an Employee too ?
So IMO its best represented as following -
class Person{
List<Role> currentRoles ;
List<Role> getCurrentRoles(){
return currentRoles ;
}
public void addRole(Role role){
currentRoles.add(role) ; // so on
}
}
Using generics and type safe casts you can easily retrieve a specific role and invoke a related operation on it.
public interface DriverRole implements Role {
License getDriversLicense() ;
}
Edit : Taking it further to answer your question fully it easily addresses a scenario where Person gains or loses a Role i.e. add or remove a Role. As pointed out in the comment here this relationship is best represented through Has <0..M> relationship then IS - kind of relationship.
Edit 1 In comparison when you use a Decorator pattern your origin gets wrapped and too many decorators can create a aggregation chain which IMO is not an ideal scenario or alternatively it will result in a decorator inheritance chain which is not to my liking.
Having said that depending on a specific scenario one particular pattern might fit better then the other though in the example you have given I think a simple aggregation of Roles is best.
Inheritance is one way to think of these relationships but this only practically makes sense when it's reasonable to enumerate the number of combinations of statuses the person might have.
Another to think of this without constraining yourself through inheritance is to think of everyone as people with different credentials.
Rather than a person being a driver, think of everyone as a generic Person having a set of credentials. eg. a drivers license, and also having a student ID. Then you can represent all of these different credentials that a person may have through a Credentials class (which Driver, Student, etc) extend. A person may have a List which you can use to perform any case based logic that you might want.
As you mentioned, consider using inheritance to approach this problem. Each Student and each Driver are both considered a Person. Therefore, both Student and Driver should inherit functionality from the parent class, Person. As far as whether someone is a student, driver, both, or just an "ordinary person" should simply be stored in a variable.
public class Person
{
private int id;
private String type;
public Person(int id, String type)
{
this.id = id;
this.type = type;
}
public int GetID()
{
// Using *this* keyword for consistency, but not necessary here.
return this.id;
}
public void SetID(int id)
{
this.id = id;
}
public String GetType()
{
// Using *this* keyword for consistency, but not necessary here.
return this.type;
}
public void SetType(String type)
{
this.type = type;
}
}
public class Student extends Person
{
super(int id, String type);
}
public class Driver extends Person
{
super(int id, String type);
}
public class Main
{
public static void main(String [] args)
{
Student sam = new Student(342918293, "student");
Driver rosa = new Driver(147284, "driver");
}
}
I'n not too fond of this being an inheritance, as a Person can be a Driver AND a Student at the same time.
Though I don't fully rememeber how the decorator pattern (sugested by Mr. Poliwhirl) works, that may be the correct approach.
This question already has answers here:
A Base Class pointer can point to a derived class object. Why is the vice-versa not true?
(13 answers)
Closed 8 years ago.
I'm a newbie to Java programming, trying to get the hang of OOP.
So I built this abstract class:
public abstract class Vehicle{....}
and 2 subclasses:
public class Car extends Vehicle{....}
public class Boat extends Vehicle{....}
Car and Boat also hold some unique fields and methods that aren't common (don't have the same name, so I can't define an abstract method for them in Vehicle).
Now in mainClass I have setup my new Garage:
Vehicle[] myGarage= new Vehicle[10];
myGarage[0]=new Car(2,true);
myGarage[1]=new Boat(4,600);
I was very happy with polymorphism until I tried to access one of the fields that are unique to Car, such as:
boolean carIsAutomatic = myGarage[0].auto;
The compiler doesn't accept that. I worked around this issue using casting:
boolean carIsAutomatic = ((Car)myGarage[0]).auto;
That works... but it doesn't help with methods, just fields. Meaning I can't do
(Car)myGarage[0].doSomeCarStuff();
So my question is - what do I really have in my garage? I'm trying to get the intuition as well as understand what's going on "behind the scenes".
for the sake of future readers, a short summary of the answers below:
Yes, there's a Car in myGarage[]
Being a static typed language, the Java compiler will not lend access to methods/fields that are non-"Vehicle", if accessing those through a data structure based on the Vehicle super class( such as Vehicle myGarage[])
As for how to solve, there are 2 main approaches below:
Use type casting, which will ease the compiler's concerns and leave any errors in the design to run time
The fact that I need casting says the design is flawed. If I need access to non-Vehicle capabilities then I shouldn't be storing the Cars and Boats in a Vehicle based data structure. Either make all those capabilities belong to Vehicle, or use more specific (derived) type based structures
In many cases, composition and/or interfaces would be a better alternative to inheritance. Probably the subject of my next question...
Plus many other good insights down there, if one does have the time to browse through the answers.
If you need to make the difference between Car and Boat in your garage, then you should store them in distinct structures.
For instance:
public class Garage {
private List<Car> cars;
private List<Boat> boats;
}
Then you can define methods that are specific on boats or specific on cars.
Why have polymorphism then?
Let's say Vehicle is like:
public abstract class Vehicle {
protected int price;
public getPrice() { return price; }
public abstract int getPriceAfterYears(int years);
}
Every Vehicle has a price so it can be put inside the Vehicle abstract class.
Yet, the formula determining the price after n years depends on the vehicle, so it left to the implementing class to define it. For instance:
public Car extends Vehicle {
// car specific
private boolean automatic;
#Override
public getPriceAfterYears(int years) {
// losing 1000$ every year
return Math.max(0, this.price - (years * 1000));
}
}
The Boat class may have an other definition for getPriceAfterYears and specific attributes and methods.
So now back in the Garage class, you can define:
// car specific
public int numberOfAutomaticCars() {
int s = 0;
for(Car car : cars) {
if(car.isAutomatic()) {
s++;
}
}
return s;
}
public List<Vehicle> getVehicles() {
List<Vehicle> v = new ArrayList<>(); // init with sum
v.addAll(cars);
v.addAll(boats);
return v;
}
// all vehicles method
public getAveragePriceAfterYears(int years) {
List<Vehicle> vehicules = getVehicles();
int s = 0;
for(Vehicle v : vehicules) {
// call the implementation of the actual type!
s += v.getPriceAfterYears(years);
}
return s / vehicules.size();
}
The interest of polymorphism is to be able to call getPriceAfterYears on a Vehicle without caring about the implementation.
Usually, downcasting is a sign of a flawed design: do not store your vehicles all together if you need to differenciate their actual type.
Note: of course the design here can be easily improved. It is just an example to demonstrate the points.
To answer your question you can find out what exactly is in your garage you do the following:
Vehicle v = myGarage[0];
if (v instanceof Car) {
// This vehicle is a car
((Car)v).doSomeCarStuff();
} else if(v instanceof Boat){
// This vehicle is a boat
((Boat)v).doSomeBoatStuff();
}
UPDATE: As you can read from the comments below, this method is okay for simple solutions but it is not good practice, particularly if you have a huge number of vehicles in your garage. So use it only if you know the garage will stay small. If that's not the case, search for "Avoiding instanceof" on stack overflow, there are multiple ways to do it.
If you operate on the base type, you can only access public methods and fields of it.
If you want to access the extended type, but have a field of the base type where it's stored (as in your case), you first have to cast it and then you can access it:
Car car = (Car)myGarage[0];
car.doSomeCarStuff();
Or shorter without temp field:
((Car)myGarage[0]).doSomeCarStuff();
Since you are using Vehicle objects, you can only call methods from the base class on them without casting. So for your garage it may be advisable to distinguish the objects in different arrays - or better lists - an array is often not a good idea, since it's far less flexible in handling than a Collection-based class.
You defined that your garage will store vehicles, so you do not care what type of vehicles you have. The vehicles have common features like engine, wheel, behavior like moving.
The actual representation of these features might be different, but at abstract layer are the same.
You used abstract class which means that some attributes, behaviors are exactly the same by both vehicle. If you want to express that your vehicles have common abstract features then use interface like moving might mean different by car and boat. Both can get from point A to point B, but in a different way (on wheel or on water - so the implementation will be different)
So you have vehicles in the garage which behave the same way and you do not car about the specific features of them.
To answer the comment:
Interface means a contract which describes how to communicate with the outer world. In the contract you define that your vehicle can move, can be steered, but you do not describe how it will actually work, it is described in the implementation.By abstract class you might have functions where you share some implementation, but you also have function which you do not know how it will be implemented.
One example of using abstract class:
abstract class Vehicle {
protected abstract void identifyWhereIAm();
protected abstract void startEngine();
protected abstract void driveUntilIArriveHome();
protected abstract void stopEngine();
public void navigateToHome() {
identifyWhereIAm();
startEngine();
driveUntilIArriveHome();
stopEngine();
}
}
You will use the same steps by each vehicle, but the implementation of the steps will differ by vehicle type. Car might use GPS, boat might use sonar to identify where it is.
I'm a newbie to Java programming, trying to get the hang of OOP.
Just my 2 cents — I will try to make it short as many interesting things have already been said. But, in fact, there is two questions here. One about "OOP" and one about how it is implemented in Java.
First of all, yes, you have a car in your garage. So your assumptions are right. But, Java is a statically typed language. And the type system in the compiler can only "know" the type of your various object by their corresponding declaration. Not by their usage. If you have an array of Vehicle, the compiler only knows that. So it will check that you only perform operation allowed on any Vehicle. (In other words, methods and attributes visible in the Vehicle declaration).
You can explain to the compiler that "you in fact know this Vehicle is a Car", by using an explicit cast (Car). the compiler will believe you -- even if in Java there is a check at run-time, that might lead to a ClassCastException that prevent further damages if you lied (other language like C++ won't check at run-time - you have to know what you do)
Finally, if you really need, you might rely of run-time type identification (i.e.: instanceof) to check the "real" type of an object before attempting to cast it. But this is mostly considered as a bad practice in Java.
As I said, this is the Java way of implementing OOP. There is whole different class family of languages broadly known as "dynamic languages", that only check at run-time if an operation is allowed on an object or not. With those languages, you don't need to "move up" all the common methods to some (possibly abstract) base class to satisfy the type system. This is called duck typing.
You asked your butler:
Jeeves, remember my garage on the Isle of Java? Go check whether the first vehicle parked there is automatic.
and lazy Jeeves said:
but sir, what if it's a vehicle that can't be automatic or non-automatic?
That's all.
Ok, that's not really all since reality is more duck-typed than statically typed. That's why I said Jeeves is lazy.
Your problem here is at a more fundamental level: you built Vehicle in such a way that Garage needs to know more about its objects than the Vehicle interface gives away. You should try and build the Vehicle class from the Garage perspective (and in general from the perspective of everything that's going to use Vehicle): what kind of things do they need to do with their vehicles? How can I make those things possible with my methods?
For example, from your example:
bool carIsAutomatic = myGarage[0].auto;
Your garage want to know about a vehicle's engine for... reasons? Anyway, there is no need for this to be just exposed by Car. You can still expose an unimplemented isAutomatic() method in Vehicle, then implement it as return True in Boat and return this.auto in Car.
It would be even better to have a three-valued EngineType enum (HAS_NO_GEARS, HAS_GEARS_AUTO_SHIFT, HAS_GEARS_MANUAL_SHIFT), which would let your code reason on the actual characteristics of a generic Vehicle cleanly and accurately. (You'd need this distinction to handle motorbikes, anyway.)
You garage contains Vehicles, so the compiler static control view that you have a Vehicle and as .auto is a Car field you can't access it, dynamically it is a Car so the cast don't create some problem, if it will be a Boat and you try to make cast to Car will rise an exception on runtime.
This is a good place for application of the Visitor design pattern.
The beauty of this pattern is you can call unrelated code on different subclasses of a superclass without having to do weird casts everywhere or putting tons of unrelated methods into the superclass.
This works by creating a Visitor object and allowing our Vehicle class to accept() the visitor.
You can also create many types of Visitor and call unrelated code using the same methods, just a different Visitor implementation, which makes this design pattern very powerful when creating clean classes.
A demo for example:
public class VisitorDemo {
// We'll use this to mark a class visitable.
public static interface Visitable {
void accept(Visitor visitor);
}
// This is the visitor
public static interface Visitor {
void visit(Boat boat);
void visit(Car car);
}
// Abstract
public static abstract class Vehicle implements Visitable {
// NO OTHER RANDOM ABSTRACT METHODS!
}
// Concrete
public static class Car extends Vehicle {
public void doCarStuff() {
System.out.println("Doing car stuff");
}
#Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// Concrete
public static class Boat extends Vehicle {
public void doBoatStuff() {
System.out.println("Doing boat stuff");
}
#Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// Concrete visitor
public static class StuffVisitor implements Visitor {
#Override
public void visit(Boat boat) {
boat.doBoatStuff();
}
#Override
public void visit(Car car) {
car.doCarStuff();
}
}
public static void main(String[] args) {
// Create our garage
Vehicle[] garage = {
new Boat(),
new Car(),
new Car(),
new Boat(),
new Car()
};
// Create our visitor
Visitor visitor = new StuffVisitor();
// Visit each item in our garage in turn
for (Vehicle v : garage) {
v.accept(visitor);
}
}
}
As you can see, StuffVisitor allows you to call different code on Boat or Car depending on which implementation of visit is called. You can also create other implementations of the Visitor to call different code with the same .visit() pattern.
Also notice that using this method, there is no use of instanceof or any hacky class checking. The only duplicated code between classes is the method void accept(Visitor).
If you want to support 3 types of concrete subclasses for example, you can just add that implementation into the Visitor interface too.
I'm really just pooling the ideas of the others here (and I'm not a Java guy, so this is pseudo rather than actual) but, in this contrived example, I would abstract my car checking approach into a dedicated class, that only knows about cars and only cares about cars when looking at garages:
abstract class Vehicle {
public abstract string getDescription() ;
}
class Transmission {
public Transmission(bool isAutomatic) {
this.isAutomatic = isAutomatic;
}
private bool isAutomatic;
public bool getIsAutomatic() { return isAutomatic; }
}
class Car extends Vehicle {
#Override
public string getDescription() {
return "a car";
}
private Transmission transmission;
public Transmission getTransmission() {
return transmission;
}
}
class Boat extends Vehicle {
#Override
public string getDescription() {
return "a boat";
}
}
public enum InspectionBoolean {
FALSE, TRUE, UNSUPPORTED
}
public class CarInspector {
public bool isCar(Vehicle v) {
return (v instanceof Car);
}
public bool isAutomatic(Car car) {
Transmission t = car.getTransmission();
return t.getIsAutomatic();
}
public bool isAutomatic(Vehicle vehicle) {
if (!isCar(vehicle)) throw new UnsupportedVehicleException();
return isAutomatic((Car)vehicle);
}
public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
if (!isCar(garage[bay])) return InspectionBoolean.UNSUPPORTED;
return isAutomatic(garage[bay])
? InspectionBoolean.TRUE
: InspectionBoolean.FALSE;
}
}
Point is, you've already decided you only care about cars when you ask about the car's transmission. So just ask the CarInspector. Thanks to the tri-state Enum, you can now know whether it is automatic or even if it is not a car.
Of course, you'll need different VehicleInspectors for each vehicle you care about. And you have just pushed the problem of which VehicleInspector to instantiate up the chain.
So instead, you might want to look at interfaces.
Abstract getTransmission out to an interface (e.g. HasTransmission). That way, you can check if a vehicle has a transmission, or write an TransmissionInspector:
abstract class Vehicle { }
class Transmission {
public Transmission(bool isAutomatic) {
this.isAutomatic = isAutomatic;
}
private bool isAutomatic;
public bool getIsAutomatic() { return isAutomatic; }
}
interface HasTransmission {
Transmission getTransmission();
}
class Car extends Vehicle, HasTransmission {
private Transmission transmission;
#Override
public Transmission getTransmission() {
return transmission;
}
}
class Bus extends Vehicle, HasTransmission {
private Transmission transmission;
#Override
public Transmission getTransmission() {
return transmission;
}
}
class Boat extends Vehicle { }
enum InspectionBoolean {
FALSE, TRUE, UNSUPPORTED
}
class TransmissionInspector {
public bool hasTransmission(Vehicle v) {
return (v instanceof HasTransmission);
}
public bool isAutomatic(HasTransmission h) {
Transmission t = h.getTransmission();
return t.getIsAutomatic();
}
public bool isAutomatic(Vehicle v) {
if (!hasTranmission(v)) throw new UnsupportedVehicleException();
return isAutomatic((HasTransmission)v);
}
public InspectionBoolean isAutomatic(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return InspectionBoolean.UNSUPPORTED;
return isAutomatic(garage[bay])
? InspectionBoolean.TRUE
: InspectionBoolean.FALSE;
}
}
Now you are saying, you only about transmission, regardless of Vehicle, so can ask the TransmissionInspector. Both the bus and the car can be inspected by the TransmissionInspector, but it can only ask about the transmission.
Now, you might decide that boolean values are not all you care about. At that point, you might prefer to use a generic Supported type, that exposes both the supported state and the value:
class Supported<T> {
private bool supported = false;
private T value;
public Supported() { }
public Supported(T value) {
this.isSupported = true;
this.value = value;
}
public bool isSupported() { return supported; }
public T getValue() {
if (!supported) throw new NotSupportedException();
return value;
}
}
Now your Inspector might be defined as:
class TransmissionInspector {
public Supported<bool> isAutomatic(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return new Supported<bool>();
return new Supported<bool>(isAutomatic(garage[bay]));
}
public Supported<int> getGearCount(Vehicle[] garage, int bay) {
if (!hasTranmission(garage[bay])) return new Supported<int>();
return new Supported<int>(getGearCount(garage[bay]));
}
}
As I've said, I'm not a Java guy, so some of the syntax above may be wrong, but the concepts should hold. Nevertheless, don't run the above anywhere important without testing it first.
If you are on Java, could use reflections to check if a function is available and execute it, too
Create Vehicle level fields that will help make each individual Vehicle more distinct.
public abstract class Vehicle {
public final boolean isCar;
public final boolean isBoat;
public Vehicle (boolean isCar, boolean isBoat) {
this.isCar = isCar;
this.isBoat = isBoat;
}
}
Set the Vehicle level fields in the inheriting class to the appropriate value.
public class Car extends Vehicle {
public Car (...) {
super(true, false);
...
}
}
public class Boat extends Vehicle {
public Boat (...) {
super(false, true);
...
}
}
Implement using the Vehicle level fields to properly decipher the vehicle type.
boolean carIsAutomatic = false;
if (myGarage[0].isCar) {
Car car = (Car) myGarage[0];
car.carMethod();
carIsAutomatic = car.auto;
}
else if (myGarage[0].isBoat) {
Boat boat = (Boat) myGarage[0];
boat.boatMethod();
}
Since your telling your compiler that everything in your garage is a Vehicle, your stuck with the Vehicle class level methods and fields. If you want to properly decipher the Vehicle type, then you should set some class level fields e.g. isCar and isBoat that will give you the programmer a better understanding of what type of Vehicle you are using.
Java is a type safe language so its best to always type check before handling data that has been casted like your Boats and Cars.
Modeling objects you want to present in a program (in order to solve some problem) is one thing, coding is another story. In your code, I think essentially it's inappropriate to model a garage using array. Arrays shouldn't be often considered as objects, although they do appear to be, usually for the sake of self-contained-ness sort of integrity of a language and providing some familiarity, but array as a type is really just a computer-specific thing, IMHO, especially in Java, where you can't extend arrays.
I understand that correctly modeling a class to represent a garage won't help answer your "cars in a garage" question; just a piece of advice.
Head back to the code. Other than getting some hang to OOP, a few questions would be helpful creating a scene hence to better understand the problem you want to resolve (assuming there is one, not just "getting some hang"):
Who or what wants to understand carIsAutomatic?
Given carIsAutomatic, who or what would perform doSomeCarStuff?
It might be some inspector, or someone who knows only how to drive auto-transmission cars, etc., but from the garage's perspective, all it knows is it holds some vehicle, therefore (in this model) it is the responsibility of this inspector or driver to tell if it's a car or a boat; at this moment, you may want to start creating another bunch of classes to represent similar types of *actor*s in the scene. Depends on the problem to be resolved, if you really have to, you can model the garage to be a super intelligent system so it behaves like a vending machine, instead of a regular garage, that has a button says "Car" and another says "Boat", so that people can push the button to get a car or a boat as they want, which in turn makes this super intelligent garage responsible for telling what (a car or a boat) should be presented to its users; to follow this improvisation, the garage may require some bookkeeping when it accepts a vehicle, someone may have to provide the information, etc., all these responsibilities go beyond a simple Main class.
Having said this much, certainly I understand all the troubles, along with the boilerplates, to code an OO program, especially when the problem it tries to resolve is very simple, but OO is indeed a feasible way to resolve many other problems. From my experience, with some input providing use cases, people start to design scenes how objects would interact with each other, categorize them into classes (as well as interfaces in Java), then use something like your Main class to bootstrap the world.
The thing is that I've been coding the following exercise and I wanted to ask you something about it:
Develop a system that meets the following requirements:
Create a test generator reminding the following functional requirements:
There are two types of questions: open and multiple choice. The first ones are textual questions that students must develop to respond. The latter are textual questions that have options for students to choose 1. Each question belongs to a topic and each topic is identified by a code and a description.
An exam has N questions and every question has an answer (entered by the student). It is important to identify the student that takes the test and the examiner (the person who assembled the exam).
In order to generate the test, the examiner must indicate the amount of questions you want for each topic. The questions are selected at random from a database of questions. The correction is made in two parts: automatic correction in multiple choice and manual correction in the open questions.
Generated tests should persist and it must be able to create a copy of each exam for each student. The student completes the test, then get the correction automatically, awaiting for manual correction by the examiner. Finally, to complete the correction, the examiner corrects the open questions.
Reports: List of exams and resolutions showing the questions and answers of each exam for each student along with it's note.
I've already coded my program, but the thing is that I have some doubts about choosing the right classes to build my project, because sometimes I can't tell if all nouns from the requirements should be classes or not, or if it just depends on the scope of the system... Reading a couple of books, I've found that we have to select only nouns that have a meaning, and for that reason we usually omit some of them.
The classes I have are the following:
public class Student {
private String name;
// methods
}
public class Exam { // the examiners create the exams
private int id;
private Examiner examiner;
private List<Question> questions = new ArrayList<Question>();
private List<Test> tests = new ArrayList<Test>();
private Map<Topic, Integer> quantityChosenPerTopic = new HashMap<Topic, Integer>();
private Map<Topic, List<Question>> questionsByTopicDisordered;
// methods
}
public class Examiner {
private String name;
// methods
}
public abstract class Question {
private Topic topic;
private String text;
// methods
}
public class OpenQuestion extends Question {
// methods
}
public class MultipleChoiceQuestion extends Question {
private List<String> options = new ArrayList<String>();
private String correct;
// methods
}
public class Test { // the students take the tests
private int number;
private Student student;
private float mark = -1;
private Map<Question, String> answers = new HashMap<Question, String>();
private Map<Question, Boolean> correction = new HashMap<Question, Boolean>();
// methods
}
public class Topic {
private int code;
private String description;
// methods
}
In the previous version of the system, I also had these classes:
public class Option {
private String option;
// methods
}
public abstract class Answer {
// methods
}
public class OpenAnswer extends Answer {
private String text;
// methods
}
public class MultipleChoiceAnswer extends Answer {
private Option option;
// methods
}
A person who helped me with this decided to take out those last classes: Option, Answer, OpenAnswer and MultipleChoiceAnswer. The reason he gave me was that it has not much sense to have them in the program because they just handle one variable and he recommended me to use them as that. And other person told me that it's important that the code works and it should be understandable by other people, plus it's not recommended to have many little classes that don't have almost nothing or very big classes with lots of code. That's why I wanted to ask you that. Thanks.
I would code it so that the resulting classes will have some behaviour, if a class consists only of information then it's better to view it as a data structure.
So, in my opinion, I would not create classes adding properties first, but their methods, by doing so you automatically exclude data structures from classes.
You said
Reading a couple of books, I've found that we have to select only nouns that have a meaning, and for that reason we usually omit some of them
...in my opinion a meaning reflect actions that could be carried out by a class, methods.
I have two classes:
public class CourseModule {
// attributes...
List<Course> courses;
public void addCourse() { ... }
}
public class Course {
// attributes...
CourseModule module;
}
The attributes of Course do not suffice to identify an object uniquely, the course module is always required, also for addition information. A CourseModule consists of difference Courses.
What I don't like here is the circular dependency, it feels wrong. Now I was thinking about the following, instead of adding courses per method and setting the CourseModule reference by hand I could automate this procedure with the constructor:
public Course(...,...,...., CourseModule module) {
this.module = module;
module.courses.add(this);
}
But again, here is another huge problem: In Brian Goetz Java Concurrency in Practice it is said: Do not let the this reference escape the constructor
So what would be best practice here? I think its a really simple example, which might bear yield a sample solution.
Funnily enough, I had to do something similar. See my question here.
I found this post on the internet that is almost an exact duplicate of what you are trying to do, also with courses! I found that the solution specified there is not 100% right, the answers on my question need to be considered.
You're right, I would say that your current setup is an example of Code Smell.
Could you not make another object CourseInfo that manages the additional course information that you say cannot be contained within Course?
public class CourseModule {
List<Course> courses;
public void addCourse(Course course) { ... }
}
public class Course {
CourseInfo info;
}
public class CourseInfo {
CourseInfo info;
}