Share
Explore

Java Programming: Dependency Injection and Interfaces to model the Business Domain

image.png

Introduction to Inheritance and Interfaces in Java

## Introduction
Welcome to our lecture on two fundamental concepts of Java and object-oriented programming:
Inheritance
Interfaces
These concepts are pivotal in designing flexible, extensible, scalable, reusable, and maintainable code.
Our goals in creating communities of OBJECTS:
Object should display High Cohesion.
Object connections (method calls) should display “low coupling”.

What is Inheritance?

Inheritance is a mechanism in Java by which one class is allowed to inherit the features (fields and methods) of another class. It provides a powerful and natural mechanism for organizing and structuring your software.
Key Points: 1. **Superclass and Subclass**:
The class whose features are inherited FROM is known as the superclass (or base class), and the class that inherits those features is known as the subclass (or derived class).
Abstraction : study the business objects // elevate commonalities to the Super Classes.
2. Code Reusability — Inheritance supports the concept of “reusability”,
i.e., when we want to create a new class and there is already a class that includes some of the code that we want, we can derive our new class from the existing class.
3. **Method Overriding**: Subclasses can override methods from their superclasses to provide specific implementations. Changing the WAY the method is implemented or delivered in a Sub Class.
4. Method Overloading: Change the METHOD Signature (parametric signature)
Polymorphism

What are Interfaces?
An interface in Java is a structural tool which requires that a class MUST deliver the methods promised in the Interface.
It has static constants and abstract methods (=> The method name and signature and return type are stated, but NOT the implement: implementation is the responsibility of whoever is writing classes which implement the Interface).
Interfaces form a contract (UML is contract based programming) between the class and the outside world, and this contract is enforced at build time by the compiler.

Key Points:
1. **Abstract Methods**: An interface contains abstract methods that the implementing class must define.
2. **Implementing Interfaces**: A class implements an interface, thereby inheriting the abstract methods of the interface.
3. **Multiple Inheritance**: Java does not support multiple inheritances with classes. However, it supports multiple inheritances through interfaces.
Multiple Inheritance is provided in Java by implementing More than One Interface onto one class.

How and Why We Use Them

Use of Inheritance
1. Code Reusability: Inheritance helps in reusing the methods and fields of the existing class. 2. Method Overriding: To provide specific implementation of superclass methods. 3. Polymorphism: Enables allowing a subclass object as if it were a superclass instance.

Example Scenario:


Consider a class `Vehicle`, and from this class, we derive subclasses such as `Car`, `Truck`, and `Motorcycle`.
These subclasses inherit common properties from `Vehicle` (like speed, fuel consumption) but also have their unique attributes and methods.

Use of Interfaces

1. Abstracting Implementation: Interfaces allow us to abstract the implementation and only expose the methods that should be visible to the user of the interface. 2. Multiple Inheritance: By implementing multiple interfaces, a class can inherit from more than one abstract type. 3. Loose Coupling: Interfaces lead to loosely coupled code by reducing the dependencies between classes.
Example Scenario: Imagine we have an interface `Drivable` with a method `drive()`.
Any class, like `Car` or `Boat`, that implements this interface, has to provide an implementation of `drive()`.
This approach provides a common protocol for different types of drivable entities.

Conclusion

Inheritance and interfaces are core components of Java that facilitate clean, manageable, and scalable code.
They allow us to build complex systems more efficiently by promoting code reusability and enforcing a structured contract for behavior. Understanding and effectively utilizing these concepts is a cornerstone in mastering Java and object-oriented programming.
Next, we will dive deeper into these concepts with practical examples and code implementation. Stay tuned!
There are 3 ways to hook up data connections and communications between objects:
1. Association/(shared field composition)
2. Method calls
3. Dependency Injection supported with Interfaces

Java lab
Exercises that focus on teaching interfaces and inheritance through different thematic examples:
---

Java Lab Exercise 1: Animals in the Jungle

Objective: Understand interfaces and inheritance using a jungle ecosystem example.
### Instructions:
1. **Create an Interface `Animal`
This interface should declare a method `makeSound()`.
2. Create Classes `Tiger`, `Monkey`, and `Snake`
These classes should implement the `Animal` interface.
Each class should override the `makeSound()` method to print a unique sound (e.g., Tiger roars).
3. Create a Class `Jungle`
This class should have a method `hearSoundsInJungle(Animal animal)` that invokes the `makeSound()` method of the given animal.
Sample Code: interface Animal { void makeSound(); }
class Tiger implements Animal { public void makeSound() { System.out.println("Roar"); } }
Let's expand this code into a fully functional Java program that illustrates polymorphism.
I'll add comments to explain what is happening at each step.
```java // Define the interface Animal with an abstract method makeSound() interface Animal { void makeSound(String whatistheSound); }
// Tiger class implements the Animal interface class Tiger implements Animal { // Implementation of makeSound() for Tiger public void makeSound(String sound) { System.out.println("Tiger says Growl!"); } }
// Monkey class also implements the Animal interface class Monkey implements Animal { // Implementation of makeSound() for Monkey public void makeSound(String sound) { System.out.println("Monkey says Cheep Cheep!"); } }
// Main class to demonstrate polymorphism public class Main { public static void main(String[] args) { // Polymorphism in action // Here, Animal is a reference type, and it can refer to any object that implements the Animal interface Animal myAnimal;
// myAnimal refers to a Tiger object myAnimal = new Tiger(); myAnimal.makeSound("Growl");
// myAnimal now refers to a Monkey object myAnimal = new Monkey(); myAnimal.makeSound("Cheep Cheep");
// This demonstrates polymorphism where a single reference (myAnimal) can take many forms (Tiger, Monkey, etc.) } } ```
### Explanation
- **Interface `Animal`**: This defines a contract (an abstract method `makeSound()`) that all classes implementing the interface must fulfill. - **Classes `Tiger` and `Monkey`**: Both classes implement the `Animal` interface, providing their own version of `makeSound()`. - **Polymorphism in Main Class**: In the `main` method, we see polymorphism in action. We declare a reference of type `Animal` and use it to refer to objects of classes `Tiger` and `Monkey`. This is the essence of polymorphism: the ability to treat objects of different classes in the same way through a common interface.
The main advantage here is flexibility. We can add more classes that implement `Animal` without changing the main class. This makes our code easily extensible and manageable, a key feature in object-oriented programming.


// Implement Monkey and Snake classes similarly
public class Jungle { void hearSoundsInJungle(Animal animal) { animal.makeSound(); }
public static void main(String[] args) { Jungle jungle = new Jungle(); jungle.hearSoundsInJungle(new Tiger()); // Test with Monkey and Snake objects } } ```
---
## Java Lab Exercise 2: Flower Families
### Objective: Learn about interfaces and inheritance through the classification of flowers.
### Instructions: 1. **Create an Interface `Flower`**: This interface should declare methods `bloom()` and `fragrance()`. 2. **Create Classes `Rose`, `Lily`, and `Daisy`**: These classes should implement the `Flower` interface. Each class should override the methods to provide specific behaviors (e.g., Rose blooms with red petals). 3. **Create a Class `Garden`**: This class should have a method `displayFlowers(Flower flower)` that invokes the `bloom()` and `fragrance()` methods of the given flower.
### Sample Code: ```java interface Flower { void bloom(); void fragrance(); }
class Rose implements Flower { public void bloom() { System.out.println("Rose blooms with red petals"); } public void fragrance() { System.out.println("Rose has a sweet fragrance"); } }
// Implement Lily and Daisy classes similarly
public class Garden { void displayFlowers(Flower flower) { flower.bloom(); flower.fragrance(); }
public static void main(String[] args) { Garden garden = new Garden(); garden.displayFlowers(new Rose()); // Test with Lily and Daisy objects } } ```
---
## Java Lab Exercise 3: Space Astronomy Schema
### Objective: Explore interfaces and inheritance using a space astronomy context.
### Instructions: 1. **Create an Interface `CelestialBody`**: This interface should declare methods `rotate()` and `revolve()`. 2. **Create Classes `Planet`, `Star`, and `Asteroid`**: These classes should implement the `CelestialBody` interface. Each class should override the methods to reflect their specific behaviors in space. 3. **Create a Class `Observatory`**: This class should have a method `studyCelestialBody(CelestialBody body)` that invokes the `rotate()` and `revolve()` methods of the given celestial body.
### Sample Code: ```java interface CelestialBody { void rotate(); void revolve(); }
class Planet implements CelestialBody { public void rotate() { System.out.println("Planet rotates on its axis"); } public void revolve() { System.out.println("Planet revolves around a star"); } }
// Implement Star and Asteroid classes similarly
public class Observatory { void studyCelestialBody(CelestialBody body) { body.rotate(); body.revolve(); }
public static void main(String[] args) { Observatory observatory = new Observatory(); observatory.studyCelestialBody(new Planet()); // Test with Star and Asteroid objects } } ```
---
These exercises provide a hands-on approach to understanding the concepts of interfaces and inheritance in Java, using interesting and relatable examples.
The three methods mentioned, association/shared field composition, method calls, and dependency injection supported with interfaces, are commonly used in object-oriented programming to establish relationships and interactions between objects.
Here is a brief explanation and support for each method:
Association/Shared Field Composition:
This method involves one class having a reference to another class as a field. Objects can share data and functionalities through this shared field. It enables one object to access and utilize the properties and methods of another object directly.
Support: Shared field composition promotes object reusability and enables easy access to related objects, fostering strong relationships between classes.
Method Calls:
Objects communicate by invoking methods on each other. This allows one object to trigger specific actions or request information from another object by calling its methods. It facilitates interaction between objects without directly accessing their internal state.
Support: Method calls promote encapsulation and abstraction by enforcing a separation of concerns. Objects communicate through well-defined interfaces, enhancing modularity and improving code maintainability.
Dependency Injection Supported with Interfaces:
Dependency Injection (DI) involves providing external dependencies or services to an object rather than creating them internally. Interfaces define the contract that classes must implement, enabling loose coupling and flexibility in switching implementations.
Support: DI with interfaces enhances testability, scalability, and maintainability of the code. It allows for easy substitution of dependencies, promotes inversion of control, and facilitates a modular and extensible design.

Let's create a Java Instruction Lab Workbook to illustrate these concepts.

This workbook will contain explanations and sample code to help you understand the implementation of association/shared field composition, method calls, and dependency injection supported with interfaces.

---
# Java Object-Oriented Programming Workbook

Chapter 1: Association/Shared Field Composition

### Concept Overview Association is a relationship where objects are linked to each other. In shared field composition, one class contains a reference to another, allowing direct interaction between their objects.
### Example Code ```java class Engine { void start() { System.out.println("Engine started."); } }
class Car { // Shared field composition private Engine engine;
Car() { this.engine = new Engine(); }
void startCar() { engine.start(); // Using the shared field } }
public class Main { public static void main(String[] args) { Car myCar = new Car(); myCar.startCar(); } } ```
### Exercise 1. Create a class `Battery` and add it as a shared field to `Car`. 2. Implement a method in `Car` to check the battery status.
---

Chapter 2: Method Calls

### Concept Overview Method calls allow objects to interact by invoking each other's methods. This mechanism facilitates communication while maintaining encapsulation.
### Example Code ```java class Printer { void printDocument(Document doc) { System.out.println(doc.getContent()); } }
class Document { private String content;
Document(String content) { this.content = content; }
String getContent() { return content; } }
public class Main { public static void main(String[] args) { Printer printer = new Printer(); Document document = new Document("Hello, World!"); printer.printDocument(document); // Method call } } ```
### Exercise 1. Add a method in `Printer` to check if it has enough ink. 2. Modify `Document` to include a method for adding a footer before printing.
---

## Chapter 3: Dependency Injection Supported with Interfaces

### Concept Overview Dependency Injection (DI) involves providing an object's dependencies from an external source rather than creating them within the object. Interfaces define a contract for these dependencies.
### Example Code ```java interface MessageService { void sendMessage(String message); }
class EmailService implements MessageService { public void sendMessage(String message) { System.out.println("Sending email: " + message); } }
class NotificationManager { private MessageService service;
// Constructor-based DI NotificationManager(MessageService service) { this.service = service; }
void notifyUser(String message) { service.sendMessage(message); } }
public class Main { public static void main(String[] args) { MessageService emailService = new EmailService(); NotificationManager manager = new NotificationManager(emailService); manager.notifyUser("Hello User!"); } } ```
### Exercise 1. Create a new class `SMSService` implementing `MessageService`. 2. Use `SMSService` in `Main` to send an SMS notification.
---

Conclusion

This workbook introduced key OOP concepts in Java through association/shared field composition, method calls, and dependency injection with interfaces. Completing the exercises will help reinforce your understanding. Keep experimenting and building on these concepts!


Demonstrate Interfaces with Method Overloading:

What if I want to overload and have void makeSound(String whatistheSound, int loudness)?


Overloading a method in Java means providing two or more different method signtures in the same class with the same name but different parameters. This can be done within the interface Animal or in the implementing classes.
Let's apply method overloading to your Animal interface and its implementations.
We'll add an overloaded method makeSound(String whatistheSound, int loudness) in the Animal interface and then implement it in both Tiger and Monkey classes:
// Define the interface Animal with two abstract methods for makeSound()
interface Animal {
void makeSound(String whatistheSound);
void makeSound(String whatistheSound, int loudness);
}

class Tiger implements Animal {
// Implementation of the first makeSound method
public void makeSound(String sound) {
System.out.println("Tiger says " + sound);
}

// Overloaded method implementation
public void makeSound(String sound, int loudness) {
System.out.println("Tiger says " + sound + " at " + loudness + " decibels");
}
}

class Monkey implements Animal {
// Implementation of the first makeSound method
public void makeSound(String sound) {
System.out.println("Monkey says " + sound);
}

// Overloaded method implementation
public void makeSound(String sound, int loudness) {
System.out.println("Monkey says " + sound + " at " + loudness + " decibels");
}
}

public class Main {
public static void main(String[] args) {
Animal myAnimal;

myAnimal = new Tiger();
myAnimal.makeSound("Growl"); // Calls the first method
myAnimal.makeSound("Growl", 80); // Calls the overloaded method

myAnimal = new Monkey();
myAnimal.makeSound("Cheep Cheep"); // Calls the first method
myAnimal.makeSound("Cheep Cheep", 50); // Calls the overloaded method
}
}

Explanation

Method Overloading: The interface Animal now contains two methods named makeSound, one that takes a single String parameter and another that takes a String and an int. This is method overloading, where methods with the same name have different parameters.
Implementing Overloaded Methods: In both Tiger and Monkey classes, both versions of the makeSound method are implemented, demonstrating how these classes handle the overloaded methods.
Polymorphism: The main method still demonstrates polymorphism by using the Animal type reference to interact with objects of different classes (Tiger and Monkey). Here, polymorphism is independent of method overloading.
Method overloading enhances the flexibility of your program, allowing methods to be called with different parameters in different situations, while polymorphism allows for dynamic method invocation.


does the interface implementing class need to implement both overloaded method versions?

No, an implementing class of an interface does not need to implement all overloaded versions of a method defined in the interface.
When you overload a method in an interface, you're simply providing multiple methods with the same name but different parameter lists.
It's not the same as method overriding, where the implementing class must provide an implementation for all abstract methods.
In your example, if you define two overloaded methods in the Animal interface (like makeSound(String sound) and makeSound(String sound, int loudness)), implementing classes are not obligated to implement both methods. They can choose to implement any one (or both) of these methods.
However, if the methods in the interface are declared as abstract (which is implicit for interfaces), then the implementing class must provide an implementation for all abstract methods. For interfaces, the concept of mandatory implementation applies to uniquely declared methods, not to each overloaded variant.
Here's a revised example to illustrate this:

interface Animal {
void makeSound(String whatistheSound);
void makeSound(String whatistheSound, int loudness);
}

class Tiger implements Animal {
public void makeSound(String sound) {
System.out.println("Tiger says " + sound);
}

// This class chooses not to implement the overloaded makeSound method with two parameters
}

class Monkey implements Animal {
public void makeSound(String sound) {
System.out.println("Monkey says " + sound);
}

// Implementing the overloaded method
public void makeSound(String sound, int loudness) {
System.out.println("Monkey says " + sound + " at " + loudness + " decibels");
}
}

public class Main {
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.