0% found this document useful (0 votes)
6 views22 pages

OOP Concepts

The document provides an overview of advanced programming concepts, specifically focusing on Object-Oriented Programming (OOP) principles such as encapsulation, inheritance, and polymorphism. It explains how encapsulation bundles data and methods into classes, inheritance allows classes to inherit properties and behaviors, and polymorphism enables objects of different classes to be treated as objects of a common superclass. The document includes examples in Java to illustrate these concepts and their practical applications.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
6 views22 pages

OOP Concepts

The document provides an overview of advanced programming concepts, specifically focusing on Object-Oriented Programming (OOP) principles such as encapsulation, inheritance, and polymorphism. It explains how encapsulation bundles data and methods into classes, inheritance allows classes to inherit properties and behaviors, and polymorphism enables objects of different classes to be treated as objects of a common superclass. The document includes examples in Java to illustrate these concepts and their practical applications.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 22

ITPE 4 – Integrative Programming Technologies 2

Lesson 1
Advanced Programming Concepts:
Object-oriented Programming (OOP) Principles and Design Patterns

OOP stands for Object-Oriented Programming. It is a programming paradigm that organizes


software design around objects, rather than functions or logic. In OOP, an object is an
instance of a class, which is a blueprint or template that defines the properties (attributes)
and behaviors (methods) of the object.

The core concepts of OOP include:

1. Encapsulation: Bundling the data (attributes) and the methods (functions) that
operate on the data into a single unit, called a class. It also involves restricting access
to certain components to prevent unintended interference and misuse of data.
2. Inheritance: Allowing a new class (child class) to inherit properties and behaviors
from an existing class (parent class), promoting code reusability.
3. Polymorphism: Enabling a single interface to represent different underlying forms
(data types). It allows for one method to be used for different types of objects.
4. Abstraction: Hiding the complex reality while exposing only the necessary parts. It
simplifies the interface by abstracting away unnecessary details, so users interact with
simplified representations.

Encapsulation is one of the four fundamental Object-Oriented Programming (OOP)


principles. It is the concept of bundling the data (attributes) and methods (behaviors) that
operate on the data into a single unit, or class, and restricting access to some of the object's
components. This is usually done by making attributes private and providing public getter
and setter methods to access and modify them.

Example of Encapsulation in Java:


Let's define a Person class where we use encapsulation to hide the internal data
(attributes) of the class and provide controlled access via public methods (getters and
setters).

1. Attributes are private.


 name: The name of the person.
 age: The age of the person.
 address: The address of the person.

2. Methods:
 Getter and Setter methods allow controlled access to the private attributes.
 getName() and setName() to get and set the name.
 getAge() and setAge() to get and set the age.
 getAddress() and setAddress() to get and set the address.

~~~
Java Code with Encapsulation:

// Person class demonstrating encapsulation in Java


public class Person {
// Private fields (attributes), hidden from outside access
private String name;
private int age;
private String address;

// Constructor to initialize Person object


public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}

// Getter method for name


public String getName() {
return name;
}

// Setter method for name


public void setName(String name) {
this.name = name;
}

// Getter method for age


public int getAge() {
return age;
}

// Setter method for age


public void setAge(int age) {
if (age > 0) { // Ensuring age is positive
this.age = age;
} else {
System.out.println("Age must be positive.");
}
}

// Getter method for address


public String getAddress() {
return address;
}

// Setter method for address


public void setAddress(String address) {
this.address = address;
}

// Method to display person details


public void displayInfo() {
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Address: " + address);
}

// Main method to test the Person class


public static void main(String[] args) {
// Creating an instance of the Person class
Person person = new Person("John Doe", 25, "123 Main St");

// Display initial person details


person.displayInfo();

// Using setter methods to update attributes


person.setName("Jane Smith");
person.setAge(30);
person.setAddress("456 Elm St");

// Display updated person details


person.displayInfo();

// Trying to set a negative age (which will be rejected)


person.setAge(-5);
}
}

Explanation of Encapsulation:
1. Private Attributes:
o The name, age, and address attributes are private. This means these fields
cannot be accessed directly from outside the class.
2. Public Getter and Setter Methods:
o We provide public getter methods (getName(), getAge(), getAddress()) to allow
access to the private attributes.
o We also provide public setter methods (setName(), setAge(), setAddress()) to
allow modification of the attributes, but with conditions and control (e.g., the
setAge() method checks if the age is positive).
3. Control over Data:
o By encapsulating the data and providing controlled access through methods, we
can ensure that the data remains valid and doesn't get set to invalid or
unexpected values (like a negative age).
~~~
Using the Person Class:

Output:
Name: John Doe
Age: 25
Address: 123 Main St
Name: Jane Smith
Age: 30
Address: 456 Elm St
Age must be positive.

Key Points:
1. Encapsulation allows us to hide the internal details of the Person class, like the name,
age, and address, from the outside world. This way, we can control how these
attributes are accessed and modified.
2. The setter methods (e.g., setAge()) can include validation logic to ensure that only
valid data is being set. In this example, we made sure that age cannot be negative.
3. The getter methods (e.g., getName()) allow safe access to the private attributes.
4. Data Integrity is maintained because we control how the data is accessed and
changed.
~~~
This is a simple demonstration of encapsulation in Java. It is a powerful mechanism that
enhances data security, code maintainability, and flexibility. By hiding the internal workings
and exposing only necessary functionality through methods, we can ensure that our objects
are used in a controlled way.
Inheritance is another fundamental concept in Object-Oriented Programming (OOP). It
allows a new class (called a subclass or child class) to inherit properties and behaviors
(fields and methods) from an existing class (called a superclass or parent class). This helps
to promote code reusability and creates a natural hierarchy between classes.
In Java, inheritance is achieved using the extends keyword.

Key Concepts:
1. Superclass: The class whose properties and behaviors are inherited. Also called the
parent class.
2. Subclass: The class that inherits the properties and behaviors from the superclass.
Also called the child class.
3. extends Keyword: Used by a subclass to inherit from a superclass.

Types of Inheritance in Java:


 Single Inheritance: A subclass inherits from one superclass.
 Multilevel Inheritance: A subclass inherits from another subclass.
 Hierarchical Inheritance: Multiple subclasses inherit from one superclass.

Java does not support multiple inheritance (i.e., a subclass cannot directly inherit from more
than one superclass) to avoid ambiguity.

Example 1: Simple Inheritance


Let's start with a simple example where a Dog class inherits from a Animal class.
Superclass (Parent Class): Animal
Subclass (Child Class): Dog

// Superclass (Parent Class)


class Animal {
// Fields (attributes)
String name;
int age;

// Constructor to initialize the animal object


public Animal(String name, int age) {
this.name = name;
this.age = age;
}

// Method of the superclass


public void sound() {
System.out.println("This animal makes a sound");
}
}

// Subclass (Child Class) inheriting from Animal


class Dog extends Animal {

// Constructor of the Dog class


public Dog(String name, int age) {
// Calling the superclass (Animal) constructor
super(name, age);
}
// Overriding the sound method (polymorphism)
@Override
public void sound() {
System.out.println(name + " barks");
}

// Additional method specific to Dog


public void fetch() {
System.out.println(name + " is fetching the ball");
}
}
public class Main {
public static void main(String[] args) {
// Creating an object of the Dog class
Dog myDog = new Dog("Buddy", 3);

// Accessing properties and methods from the superclass (Animal)


System.out.println("Name: " + myDog.name);
System.out.println("Age: " + myDog.age);

// Calling the overridden method


myDog.sound(); // Output: Buddy barks

// Calling a method specific to Dog


myDog.fetch(); // Output: Buddy is fetching the ball
}
}

Output:
Name: Buddy
Age: 3
Buddy barks
Buddy is fetching the ball

Explanation:
1. Inheritance: The Dog class inherits from the Animal class, so it automatically has the
name and age attributes and the sound() method from the Animal class.
2. The Dog class has its own constructor and method (fetch()).
3. The sound() method is overridden in the Dog class to provide a more specific
implementation for dogs.
4. The super(name, age) call is used to invoke the constructor of the Animal class to
initialize the inherited attributes.
~~~
Example 2: Multilevel Inheritance
In multilevel inheritance, a subclass inherits from another subclass, forming a chain of
inheritance.
// Base class (Parent Class)
class Animal {
String name;

public Animal(String name) {


this.name = name;
}

public void sound() {


System.out.println(name + " makes a sound");
}
}

// Derived class (Child Class) inheriting from Animal


class Dog extends Animal {
public Dog(String name) {
super(name);
}

@Override
public void sound() {
System.out.println(name + " barks");
}
}

// Further Derived class (Grandchild Class) inheriting from Dog


class Puppy extends Dog {
public Puppy(String name) {
super(name);
}

@Override
public void sound() {
System.out.println(name + " yaps");
}
}

public class Main {


public static void main(String[] args) {
Puppy myPuppy = new Puppy("Max");

// Calling methods inherited from Animal and Dog


myPuppy.sound(); // Output: Max yaps
}
}

Output:
Max yaps

Explanation:
 Multilevel Inheritance: The Puppy class inherits from the Dog class, which in turn
inherits from the Animal class. Each subclass can override the methods of its
superclass, resulting in a method resolution order where the most specific method is
called.
 The Puppy class calls its own sound() method, which overrides the sound() method
from Dog and Animal.
~~~
Example 3: Hierarchical Inheritance
In hierarchical inheritance, multiple subclasses inherit from a single superclass.

// Superclass (Parent Class)


class Animal {
public void sound() {
System.out.println("This animal makes a sound");
}
}

// Subclass 1
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
}
}

// Subclass 2
class Cat extends Animal {
@Override
public void sound() {
System.out.println("Cat meows");
}
}

public class Main {


public static void main(String[] args) {
Dog myDog = new Dog();
Cat myCat = new Cat();

myDog.sound(); // Dog barks


myCat.sound(); // Cat meows
}
}

Output:
Dog barks
Cat meows

Explanation:
 Hierarchical Inheritance: Both Dog and Cat inherit from the Animal class, but each
provides its own implementation of the sound() method.
 This allows multiple subclasses to share the same functionality from the superclass
but customize it as needed.

~~~
Advantages of Inheritance:
1. Code Reusability: Common code in the superclass can be reused by subclasses,
reducing redundancy.
2. Method Overriding: Subclasses can override superclass methods to provide specific
behavior, enhancing flexibility.
3. Extensibility: New functionality can be added by creating subclasses, which can
extend the functionality of existing code.
4. Simplified Code: It creates a natural hierarchy between related classes, making the
system easier to understand and maintain.
Conclusion:
 Inheritance allows classes to inherit features (fields and methods) from other classes.
 It helps to achieve code reuse, extendibility, and organization.
 In Java, inheritance is implemented using the extends keyword, and classes can inherit
from one superclass at a time (single inheritance), forming a natural hierarchy.

Polymorphism is one of the fundamental concepts in Object-Oriented Programming (OOP). It


allows objects of different classes to be treated as objects of a common superclass. The most
common types of polymorphism are:
1. Compile-time polymorphism (or Method Overloading).
2. Runtime polymorphism (or Method Overriding).
Examples for both types of polymorphism in Java.

Compile-time Polymorphism (Method Overloading)


Method Overloading occurs when multiple methods with the same name exist within
the same class, but they have different parameter lists (either different types or different
numbers of parameters). The correct method is chosen at compile time based on the method
signature.

Example: Method Overloading

class Calculator {

// Method to add two integers


public int add(int a, int b) {
return a + b;
}

// Method to add three integers (overloaded method)


public int add(int a, int b, int c) {
return a + b + c;
}

// Method to add two doubles (overloaded method)


public double add(double a, double b) {
return a + b;
}
}

public class Main {


public static void main(String[] args) {
Calculator calc = new Calculator();

// Using the add method with two integers


System.out.println("Sum of 2 integers: " + calc.add(5, 10));

// Using the add method with three integers


System.out.println("Sum of 3 integers: " + calc.add(5, 10, 15));
// Using the add method with two doubles
System.out.println("Sum of 2 doubles: " + calc.add(5.5, 10.5));
}
}
Output:
Sum of 2 integers: 15
Sum of 3 integers: 30
Sum of 2 doubles: 16.0

Explanation:
 We have overloaded the add() method with different parameter lists.
 The method is called with two integers, three integers, and two doubles. The correct
method is selected at compile time based on the arguments passed.

~~~
Runtime Polymorphism (Method Overriding)

Method Overriding happens when a subclass provides a specific implementation of a method


that is already defined in its superclass. The method in the subclass has the same signature
(name and parameters) as the method in the superclass.

At runtime, the JVM determines which method to call based on the object's type (i.e.,
whether it's an instance of the subclass or superclass).

// Superclass
class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
// Subclass 1
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
}
}
// Subclass 2
class Cat extends Animal {
@Override
public void sound() {
System.out.println("Cat meows");
}
}
public class Main {
public static void main(String[] args) {
// Creating objects of subclass types
Animal myAnimal = new Animal();
Animal myDog = new Dog();
Animal myCat = new Cat();

// Calling the sound method (polymorphism in action)


myAnimal.sound(); // Output: Animal makes a sound
myDog.sound(); // Output: Dog barks
myCat.sound(); // Output: Cat meows
}
}

Output:
Animal makes a sound
Dog barks
Cat meows

Explanation:
 The Animal class has a method sound(), and both the Dog and Cat subclasses override
this method.
 The method call myAnimal.sound(), myDog.sound(), and myCat.sound() is determined
at runtime. Even though all three variables are of type Animal, the method
corresponding to the actual object type (whether Dog or Cat) is called, which is
runtime polymorphism.

Key Points:
 Compile-time polymorphism (method overloading) is resolved at compile time.
 Runtime polymorphism (method overriding) is resolved at runtime based on the
object's actual type.
 In both cases, polymorphism allows us to write more flexible and reusable code.

Conclusion:
Polymorphism allows for flexibility in programming, as it enables methods or functions
to behave differently based on the object type. It enhances code maintainability and
readability, as you can reuse method names while changing behavior for different object
types.

Abstraction is one of the core concepts of Object-Oriented Programming (OOP). It refers to


the process of hiding the implementation details and exposing only the necessary and
relevant information to the user. The goal of abstraction is to simplify complex systems by
reducing the amount of detail the user needs to understand in order to interact with the
system effectively.

Key Points About Abstraction:


1. Hiding Complexity: Abstraction hides the complex implementation details from the
user, showing only the essential features.
2. Focus on What, Not How: It focuses on what the object can do, rather than how it does
it.
3. Use of Abstract Classes and Interfaces: In Java, abstraction is typically achieved
through abstract classes and interfaces.
4. Provides a Clear Interface: Users interact with objects through clear, simple interfaces
without needing to understand the underlying implementation.

Examples of Abstraction in Java

1. Using Abstract Classes


An abstract class is a class that cannot be instantiated directly. It can have both abstract
methods (methods without a body) and concrete methods (methods with a body). The
subclasses of the abstract class must implement the abstract methods.

Example: Abstract Class


// Abstract class
abstract class Animal {
// Abstract method (does not have a body)
public abstract void sound();

// Regular method with a body


public void sleep() {
System.out.println("This animal is sleeping");
}
}

// Subclass (inherited from Animal)


class Dog extends Animal {
// Implementing the abstract method
@Override
public void sound() {
System.out.println("Dog barks");
}
}

// Another subclass (inherited from Animal)


class Cat extends Animal {
// Implementing the abstract method
@Override
public void sound() {
System.out.println("Cat meows");
}
}

public class Main {


public static void main(String[] args) {
// We cannot create an object of the abstract class
// Animal myAnimal = new Animal(); // Error!

// But we can create objects of the subclasses


Animal myDog = new Dog();
Animal myCat = new Cat();

// Calling methods on the objects


myDog.sound(); // Dog barks
myDog.sleep(); // This animal is sleeping

myCat.sound(); // Cat meows


myCat.sleep(); // This animal is sleeping
}
}
Output:
Dog barks
This animal is sleeping
Cat meows
This animal is sleeping

Explanation:
 The Animal class is abstract and has one abstract method sound().
 The Dog and Cat classes inherit from Animal and provide their own implementation of
the sound() method.
 The user of the Dog or Cat classes doesn’t need to know how the sound() method is
implemented, only that it will make a sound.

~~~
2. Using Interfaces
An interface is another way to achieve abstraction. Unlike abstract classes, an interface
cannot contain any concrete methods (methods with a body). An interface defines only
method signatures, and the classes that implement the interface must provide the
implementation of the methods.

Example: Interface
// Interface
interface Shape {
// Abstract method (does not have a body)
void draw();
}

// Implementing the interface in a class


class Circle implements Shape {
// Providing implementation of the abstract method
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}

// Another class implementing the same interface


class Rectangle implements Shape {
// Providing implementation of the abstract method
@Override
public void draw() {
System.out.println("Drawing a Rectangle");
}
}

public class Main {


public static void main(String[] args) {
Shape myCircle = new Circle();
Shape myRectangle = new Rectangle();

// Calling the draw method on different shapes


myCircle.draw(); // Drawing a Circle
myRectangle.draw(); // Drawing a Rectangle
}
}
Output:
Drawing a Circle
Drawing a Rectangle

Explanation:
 The Shape interface defines a method draw().
 Both the Circle and Rectangle classes implement the Shape interface and provide
their own implementation of the draw() method.
 The user of the Shape interface doesn’t need to know how the draw() method works,
just that it will be able to draw the shape.

Benefits of Abstraction:
1. Reduces Complexity: Abstraction hides unnecessary details, simplifying the code for
the user.
2. Improves Code Reusability: With abstract classes and interfaces, you can write
reusable and modular code.
3. Encourages Loose Coupling: Abstraction allows changes to the implementation
without affecting the interface or the users of the class.
4. Enhances Maintainability: By focusing on essential functionality and hiding the
complexity, the code is easier to maintain and extend.

Summary:
 Abstraction allows you to focus on what an object does rather than how it does it.
 It is achieved through abstract classes and interfaces in Java.
 Abstraction hides the implementation details and exposes only the necessary
functionalities to the user.

Design Patterns in Java

Design patterns are common solutions to recurring problems in software design. They
provide standardized ways to solve problems and help developers write code that is
reusable, maintainable, and scalable. In Java (or any other object-oriented programming
language), design patterns are particularly useful for organizing code and creating systems
that are easier to extend and maintain.

Why Use Design Patterns?

1. Reusability: Design patterns solve common problems in a standardized way. Once you
learn a pattern, you can apply it across different projects.
2. Scalability: Patterns are often designed to allow easy expansion or modification of a
system.
3. Maintainability: By using well-understood solutions, design patterns make it easier to
maintain and extend code.
4. Communication: Design patterns provide a common vocabulary for developers to
discuss solutions and ideas.

Types of Design Patterns in Java

Design patterns are typically classified into three main categories:


1. Creational Patterns
2. Structural Patterns
3. Behavioral Patterns
Creational Patterns
These patterns deal with the process of object creation. They help abstract the
instantiation process and make the system more flexible.
 Singleton: Ensures that a class has only one instance and provides a global point of
access to it. It’s often used for managing shared resources like database connections
or configurations.
 Factory Method: Defines an interface for creating an object but allows subclasses to
alter the type of objects that will be created.

Singleton
Purpose: Ensures that a class has only one instance and provides a global point of
access to that instance.

When to Use: When you need to control access to shared resources, like a database
connection or configuration.

Explanation:
1. Private static variable (instance):
o The instance variable is private and static, meaning there will be only one
instance of the Singleton class, and it will be shared across the entire
application.
o The static keyword ensures that the instance is tied to the class itself, rather
than to any individual object. So, no matter how many times you call
getInstance(), you'll always get the same Singleton instance.
2. Private constructor (private Singleton()):
o The constructor is marked as private to prevent external classes from creating
new instances of the Singleton class directly. This enforces the rule that only
one instance of Singleton will exist.
o You can't instantiate Singleton outside of the Singleton class itself.
3. Public static method (getInstance()):
o This method provides the global access point for obtaining the single instance of
Singleton.
o The getInstance() method checks if the instance is null. If it is, it creates a new
Singleton instance. This ensures that only one instance is created during the
lifetime of the application (i.e., lazy initialization).
o If the instance already exists, it simply returns the existing instance.
4. Example method (showMessage()):
This is a regular instance method in the Singleton class, which demonstrates
some functionality of the singleton. In this case, it prints a message to the
o

console.
o This method can be called on the single instance of Singleton.

How the Singleton Pattern Works:


 Single Instance: The pattern ensures that only one instance of the Singleton class can
ever be created. This instance is shared across the entire program, which helps in
managing resources or ensuring consistency (e.g., settings, logging, etc.).
 Lazy Initialization: The instance of the Singleton class is created only when it's needed
(i.e., the first time getInstance() is called). This is an example of lazy initialization.
 Global Access: The static method getInstance() provides global access to the single
instance, ensuring that it can be accessed from anywhere in the program.

Benefits of the Singleton Pattern:


 Controlled Access: You can ensure that only one instance of the class is created and
used.
 Global Access: The single instance can be accessed globally throughout the
application.
 Reduced Memory Usage: Since only one instance exists, the memory footprint is
minimal compared to creating multiple instances of the same class.
Drawbacks:
 Global State: Singleton can introduce a global state into the application, making it
harder to manage and test.
 Hidden Dependencies: It can hide class dependencies, making the system harder to
understand and maintain.
 Difficulty in Unit Testing: Since a Singleton is often static and global, it can be difficult
to mock or replace during testing.

Overall, the Singleton pattern is most effective when you truly need to manage global state
or resources and are sure that a single instance is sufficient for the task at hand.
Example:
This Java code gets an input string the user and displaying it five times in the screen
using Singleton design pattern.

import java.util.Scanner;

// Singleton class to manage string display


class StringDisplay {
private static StringDisplay instance;
private String message;

// Private constructor to prevent instantiation


private StringDisplay() {}

// Method to get the single instance of StringDisplay


public static StringDisplay getInstance() {
if (instance == null) {
instance = new StringDisplay();
}
return instance;
}

// Method to set the message


public void setMessage(String message) {
this.message = message;
}

// Method to display the message five times


public void displayMessage() {
for (int i = 0; i < 5; i++) {
System.out.println(message);
}
}
}

public class Main {


public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("Enter a string: ");

String userInput = scanner.nextLine();

// Get the singleton instance and set the message


StringDisplay stringDisplay = StringDisplay.getInstance();
stringDisplay.setMessage(userInput);

// Display the message five times


stringDisplay.displayMessage();

scanner.close();
}
}
Code Explanation

In this Java program, we implement the Singleton design pattern through


the StringDisplay class. Here’s a breakdown of the code:

1. Singleton Class: The StringDisplay class is designed to ensure that only one instance
of it can exist. This is achieved by:

o A private static variable instance that holds the single instance of the class.

o A private constructor that prevents external instantiation.

A public static method getInstance() that checks if the instance is null; if so, it
creates a new instance.
o

2. Setting the Message: The setMessage(String message) method allows us to set the
string that we want to display.

3. Displaying the Message: The displayMessage() method contains a loop that prints the
stored message five times to the console.

4. Main Class: In the Main class:

o We use a Scanner to read user input from the console.

We retrieve the singleton instance of StringDisplay, set the user input as the
message, and then call displayMessage() to print it five times.
o

This implementation not only adheres to the Singleton pattern but also provides a clear and
efficient way to manage the display of a string input by the user.

Factory Method Pattern

Purpose: Defines an interface for creating objects, but lets subclasses alter the
type of objects that will be created.

Example: A method that creates different types of shapes (Circle, Square, etc.)
depending on the input.

Explanation:

1. Abstract Class Shape


This is an abstract class. An abstract class cannot be instantiated directly (i.e., you can't
create an object of Shape directly). Instead, it provides a blueprint for other classes to
inherit from.
 The draw() method is defined as abstract in the Shape class. This means that any
concrete subclass of Shape must implement the draw() method. It doesn't have a body
here; it must be implemented by the subclasses.

2. Circle and Square Classes


These are concrete classes that extend the Shape abstract class.
 Circle and Square provide their own implementations of the draw() method.
 When you call the draw() method on an object of Circle or Square, it will print out
"Drawing Circle" or "Drawing Square", respectively.

3. Abstract Class ShapeFactory


ShapeFactory is another abstract class. This class defines a factory method createShape()
but does not implement it. The method will return an object of type Shape. The goal of
ShapeFactory is to provide a common interface for creating shapes.
 Since it's abstract, you cannot instantiate ShapeFactory directly. Instead, you will
create specific concrete subclasses of ShapeFactory to define how objects of Shape
(like Circle or Square) are created.

4. CircleFactory and SquareFactory Classes


These are concrete subclasses of the ShapeFactory class. Both CircleFactory and
SquareFactory implement the createShape() method:
 CircleFactory creates and returns a new Circle object when createShape() is called.
 SquareFactory creates and returns a new Square object when createShape() is called.

How Does This Work Together?


1. Abstract Class Shape defines a contract (draw() method) that subclasses must
implement.
2. Concrete classes Circle and Square implement the draw() method, providing specific
behavior for each shape.
3. Abstract class ShapeFactory defines the method createShape(), but leaves the
implementation up to its subclasses.
4. Concrete factories CircleFactory and SquareFactory implement createShape(),
deciding which shape object (Circle or Square) to create.
Example:
This Java code implements a Factory Method Design Pattern to create different types
of shapes such as Circle and Square based on user input.

import java.util.Scanner;

// Shape interface
interface Shape {
void draw();
}
// Circle class implementing Shape
class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Circle");
}
}
// Square class implementing Shape
class Square implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Square");
}
}
// ShapeFactory class
abstract class ShapeFactory {
public abstract Shape createShape();
}
// CircleFactory class
class CircleFactory extends ShapeFactory {
@Override
public Shape createShape() {
return new Circle();
}
}
// SquareFactory class
class SquareFactory extends ShapeFactory {
@Override
public Shape createShape() {
return new Square();
}
}
// Client code
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Enter the shape you want to create (Circle/Square): ");
String shapeType = scanner.nextLine();

ShapeFactory factory;
Shape shape = null;

switch (shapeType.toLowerCase()) {
case "circle":
factory = new CircleFactory();
shape = factory.createShape();
break;
case "square":
factory = new SquareFactory();
shape = factory.createShape();
break;
default:
System.out.println("Invalid shape type.");
return;
}

shape.draw();
scanner.close();
}
Code Explanation
In the provided code, Factory Method Design Pattern was implement to create different
shapes. Here’s a breakdown of the components:
1. Shape Interface: This is a common interface for all shapes. It declares a
method draw() that each shape class must implement.
2. Circle and Square Classes: These classes implement the Shape interface. Each class
provides its own implementation of the draw() method, which outputs a message
indicating the shape being drawn.
3. ShapeFactory Abstract Class: This abstract class defines a method createShape(String
shapeType). Subclasses will implement this method to create specific shapes.
4. ConcreteShapeFactory Class: This class extends ShapeFactory and provides the
implementation for the createShape method. It checks the input string and returns an
instance of the corresponding shape (Circle or Square). If the input does not match
any known shape, it returns null.
5. Client Code (ShapeFactoryDemo): This is the entry point of the application. It creates
an instance of ConcreteShapeFactory and uses it to create shapes based on user input.
The draw() method is then called on each shape to demonstrate the functionality.

This design pattern not only simplifies the process of object creation but also adheres to the
Open/Closed Principle, allowing for easy extension of new shapes without modifying existing
code.

You might also like