0% found this document useful (0 votes)
17 views18 pages

Aoop Skill Week-2

The document discusses the implementation of various design patterns in Java applications, including Singleton, Factory, and Abstract Factory patterns. It provides code examples for a License Manager, Banking System Manager, Report generation, Postfix expression evaluation, and a vehicle manufacturing system, highlighting the benefits of each pattern in terms of scalability, flexibility, and maintainability. Additionally, it outlines the design of a Logger Management System that utilizes both Singleton and Factory patterns for efficient logging.

Uploaded by

sonusri778
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views18 pages

Aoop Skill Week-2

The document discusses the implementation of various design patterns in Java applications, including Singleton, Factory, and Abstract Factory patterns. It provides code examples for a License Manager, Banking System Manager, Report generation, Postfix expression evaluation, and a vehicle manufacturing system, highlighting the benefits of each pattern in terms of scalability, flexibility, and maintainability. Additionally, it outlines the design of a Logger Management System that utilizes both Singleton and Factory patterns for efficient logging.

Uploaded by

sonusri778
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 18

AOOP SKILL WEEK 2

KATURI SONU SRI


2300032941

1. In a Java application that requires license validation, explain how you


can use the Singleton Design Pattern to implement a License Manager.
Describe the benefits of using a Singleton for managing the license state
and validation process. Provide a sample implementation of a License
Manager class that ensures only one instance is responsible for
checking and managing license status throughout the application.

CODE:

public class LicenseManager {

private static LicenseManager instance;

private boolean isLicenseValid;

private LicenseManager() {

this.isLicenseValid = false;

public static synchronized LicenseManager getInstance() {

if (instance == null) {

instance = new LicenseManager();

return instance;

public synchronized void validateLicense(String licenseKey) {

if ("VALID_LICENSE_KEY".equals(licenseKey)) {

isLicenseValid = true;

System.out.println("License validated successfully.");

} else {

isLicenseValid = false;

System.out.println("Invalid license key.");


}

public synchronized boolean isLicenseValid() {

return isLicenseValid;

public synchronized void resetLicense() {

isLicenseValid = false;

System.out.println("License has been reset.");

public static void main(String[] args) {

LicenseManager licenseManager = LicenseManager.getInstance();

licenseManager.validateLicense("VALID_LICENSE_KEY");

System.out.println("Is license valid? " + licenseManager.isLicenseValid());

licenseManager.resetLicense();

System.out.println("Is license valid? " + licenseManager.isLicenseValid());

}
2. In a Java application for handling banking transactions, how can the
Singleton Design Pattern be used to create a BankingSystemManager?
Explain how this pattern helps ensure there is only one instance
managing banking operations like account balance and transactions.
Provide a clear and simple example of a BankingSystemManager class
that uses Singleton to guarantee a single instance and handle operations
safely in a multi-threaded environment.

CODE:

import java.util.concurrent.ConcurrentHashMap;

public class BankingSystemManager {


private static BankingSystemManager instance;

private ConcurrentHashMap<String, Double> accountBalances;

private BankingSystemManager() {
accountBalances = new ConcurrentHashMap<>();
}

public static synchronized BankingSystemManager getInstance() {


if (instance == null) {
instance = new BankingSystemManager();
}
return instance;
}

public synchronized void createAccount(String accountNumber, double initialBalance) {


if (accountBalances.containsKey(accountNumber)) {
System.out.println("Account already exists: " + accountNumber);
} else {
accountBalances.put(accountNumber, initialBalance);
System.out.println("Account created: " + accountNumber + " with balance " +
initialBalance);
}
}

public synchronized void deposit(String accountNumber, double amount) {


if (accountBalances.containsKey(accountNumber)) {
accountBalances.put(accountNumber, accountBalances.get(accountNumber) +
amount);
System.out.println("Deposited " + amount + " to account " + accountNumber);
} else {
System.out.println("Account not found: " + accountNumber);
}
}

public synchronized void withdraw(String accountNumber, double amount) {


if (accountBalances.containsKey(accountNumber)) {
double currentBalance = accountBalances.get(accountNumber);
if (currentBalance >= amount) {
accountBalances.put(accountNumber, currentBalance - amount);
System.out.println("Withdrew " + amount + " from account " + accountNumber);
} else {
System.out.println("Insufficient balance in account " + accountNumber);
}
} else {
System.out.println("Account not found: " + accountNumber);
}
}

public synchronized double checkBalance(String accountNumber) {


return accountBalances.getOrDefault(accountNumber, 0.0);
}

public static void main(String[] args) {


BankingSystemManager manager = BankingSystemManager.getInstance();

manager.createAccount("12345", 1000.0);
manager.createAccount("67890", 500.0);

manager.deposit("12345", 200.0);
manager.withdraw("12345", 300.0);

System.out.println("Balance in account 12345: " + manager.checkBalance("12345"));

manager.withdraw("11111", 100.0);
}
}

SAMPLE OUTPUT:
3. In a reporting application, users need to generate reports in different
formats such as PDF, Excel, and HTML. Each report format requires
a different implementation for generating the report. a) Design a
ReportFactory class that uses the Factory Design Pattern to create
instances of the appropriate Report type based on user input. b)
Provide the code implementation for the Report, PDFReport,
ExcelReport, and HTMLReport classes. c) Explain how the Factory
Design Pattern helps in managing the creation of different report types
and how it enhances the flexibility and scalability of the reporting
system.

a) Design of ReportFactory

The ReportFactory class will:


1. Provide a method to return an instance of the appropriate Report type based on user
input.
2. Decouple the client code from the instantiation logic of specific report types.

b)
interface Report {
void generateReport();
}

class PDFReport implements Report {


public void generateReport() {
System.out.println("Generating PDF report...");
}
}

class ExcelReport implements Report {

public void generateReport() {


System.out.println("Generating Excel report...");
}
}

class HTMLReport implements Report {


public void generateReport() {
System.out.println("Generating HTML report...");
}
}

class ReportFactory {
public static Report createReport(String reportType) {
switch (reportType.toLowerCase()) {
case "pdf":
return new PDFReport();
case "excel":
return new ExcelReport();
case "html":
return new HTMLReport();
default:
throw new IllegalArgumentException("Invalid report type: " + reportType);
}
}
}

public class ReportFactoryDemo {


public static void main(String[] args) {
String userInput = "pdf";

Report report = ReportFactory.createReport(userInput);

report.generateReport();
}
}

c) How the Factory Design Pattern Helps


1. Centralized Object Creation: The ReportFactory centralizes the creation logic,
ensuring that all instantiation rules are defined in one place.
2. Encapsulation: The client code does not need to know the details of how the reports
are implemented or created. It simply requests a report by type.
3. Scalability: Adding a new report type is easy. Simply create a new class implementing
Report and update the ReportFactory to handle the new type.
4. Flexibility: The system can adapt to user requirements without modifying client code.
For example, new formats like "CSV" or "JSON" can be added without affecting existing
functionality.
5. Code Reusability: The factory logic is reusable across multiple parts of the application,
avoiding duplication of report creation logic.

SAMPLE OUTPUT:
4. You are tasked with implementing a calculator that evaluates postfix
expressions (Reverse Polish Notation). The postfix expression will be
provided as input, and the output should be the result of the
expression. a) Define an abstract Operation class that represents a
generic operation (e.g., addition, subtraction, multiplication, division)
for evaluating postfix expressions. Implement concrete classes for
each specific operation (e.g., Addition, Subtraction, Multiplication,
Division). b) Create a OperationFactory class that uses the Factory
Design Pattern to instantiate the appropriate Operation objects based
on the operator encountered in the postfix expression. c) Implement a
PostfixEvaluator class that utilizes the OperationFactory to evaluate a
given postfix expression. The class should handle the input string
(e.g., "7 3 - 2 1 + *") and produce the correct output (e.g., 12). d)
Explain how the Factory Design Pattern is utilized in your
implementation to handle different operations dynamically and how it
contributes to the design's flexibility and maintainability.

a) Abstract Operation Class and Concrete Implementations


abstract class Operation {
public abstract double evaluate(double operand1, double operand2);
}

class Addition extends Operation {


public double evaluate(double operand1, double operand2) {
return operand1 + operand2;
}
}

class Subtraction extends Operation {


public double evaluate(double operand1, double operand2) {
return operand1 - operand2;
}
}

class Multiplication extends Operation {


public double evaluate(double operand1, double operand2) {
return operand1 * operand2;
}
}

class Division extends Operation {

public double evaluate(double operand1, double operand2) {


if (operand2 == 0) {
throw new ArithmeticException("Division by zero");
}
return operand1 / operand2;
}
}

b) OperationFactory Class

class OperationFactory {
public static Operation getOperation(String operator) {
switch (operator) {
case "+":
return new Addition();
case "-":
return new Subtraction();
case "*":
return new Multiplication();
case "/":
return new Division();
default:
throw new IllegalArgumentException("Invalid operator: " + operator);
}
}
}

c) PostfixEvaluator Class
import java.util.Stack;

public class PostfixEvaluator {

public static double evaluate(String expression) {


Stack<Double> stack = new Stack<>();
String[] tokens = expression.split("\\s+");

for (String token : tokens) {


if (isOperator(token)) {
double operand2 = stack.pop();
double operand1 = stack.pop();

Operation operation = OperationFactory.getOperation(token);

double result = operation.evaluate(operand1, operand2);


stack.push(result);
} else {
stack.push(Double.parseDouble(token));
}
}

return stack.pop();
}

private static boolean isOperator(String token) {


return "+-*/".contains(token);
}

public static void main(String[] args) {


String expression = "7 3 - 2 1 + *"; // Postfix expression
double result = evaluate(expression);
System.out.println("Result of the postfix expression: " + result);
}
}

d) How the Factory Design Pattern Contributes


1. Dynamic Handling of Operations: The OperationFactory dynamically determines the
correct operation based on the operator in the expression. This eliminates the need for
hardcoding logic for each operation within the evaluator.
2. Extensibility: Adding a new operation (e.g., modulus or exponentiation) requires only
implementing a new Operation subclass and updating the OperationFactory. The rest of
the code remains untouched, adhering to the Open/Closed Principle.
3. Encapsulation: The logic for each operation is encapsulated within its respective class,
making the design modular and easier to test or modify.
4. Maintainability: Centralizing operation creation in the factory simplifies updates to the
logic for creating operations, reducing duplication and potential errors.

SAMPLE OUTPUT:
5. In the context of developing a sophisticated vehicle manufacturing
system that produces different types of vehicles such as cars, trucks,
and motorcycles, each with its own distinct set of components and
assembly processes, how can the Abstract Factory design pattern be
utilized to manage the creation of these components? a) Design an
abstract factory interface VehicleFactory for this system. What
methods should this interface include to facilitate the creation of
components specific to each type of vehicle? b) Implement concrete
factories for each type of vehicle: CarFactory, TruckFactory, and
MotorcycleFactory. Describe the specific components each factory
should produce and how these components differ from one another.
c) Discuss how the Abstract Factory pattern helps in achieving
separation of concerns in the vehicle manufacturing system. What
advantages does it offer in terms of scalability and flexibility when
adding new vehicle types or components? d) Provide a code example
interface and its concrete implementations to assemble a vehicle.
Demonstrate how the client can work with different factories to
assemble different types of vehicles without changing the core logic.

a) Design of VehicleFactory Interface


The VehicleFactory interface defines methods for creating components specific to each type
of vehicle. Each vehicle type (Car, Truck, Motorcycle) has its own set of components, such
as an engine, wheels, and a chassis.
public interface VehicleFactory {
Engine createEngine();
Wheels createWheels();
Chassis createChassis();
}

b) Concrete Factories
Each concrete factory implements the VehicleFactory interface to produce components
specific to a vehicle type.
CarFactory
public class CarFactory implements VehicleFactory {
public Engine createEngine() {
return new CarEngine();
}

public Wheels createWheels() {


return new CarWheels();
}

public Chassis createChassis() {


return new CarChassis();
}
}
TruckFactory
public class TruckFactory implements VehicleFactory {

public Engine createEngine() {


return new TruckEngine();
}

public Wheels createWheels() {


return new TruckWheels();
}

public Chassis createChassis() {


return new TruckChassis();
}
}
MotorcycleFactory
public class MotorcycleFactory implements VehicleFactory {
public Engine createEngine() {
return new MotorcycleEngine();
}

public Wheels createWheels() {


return new MotorcycleWheels();
}

public Chassis createChassis() {


return new MotorcycleChassis();
}
}
Components (Common Interface and Specific Implementations)
interface Engine {
void assemble();
}

interface Wheels {
void assemble();
}

interface Chassis {
void assemble();
}
class CarEngine implements Engine {
public void assemble() {
System.out.println("Assembling car engine.");
}
}

class CarWheels implements Wheels {


public void assemble() {
System.out.println("Assembling car wheels.");
}
}

class CarChassis implements Chassis {

public void assemble() {


System.out.println("Assembling car chassis.");
}
}

class TruckEngine implements Engine {


public void assemble() {
System.out.println("Assembling truck engine.");
}
}

class TruckWheels implements Wheels {

public void assemble() {


System.out.println("Assembling truck wheels.");
}
}

class TruckChassis implements Chassis {

public void assemble() {


System.out.println("Assembling truck chassis.");
}
}

class MotorcycleEngine implements Engine {


public void assemble() {
System.out.println("Assembling motorcycle engine.");
}
}

class MotorcycleWheels implements Wheels {


public void assemble() {
System.out.println("Assembling motorcycle wheels.");
}
}

class MotorcycleChassis implements Chassis {


public void assemble() {
System.out.println("Assembling motorcycle chassis.");
}
}

c) Advantages of Abstract Factory Pattern


1. Separation of Concerns: The Abstract Factory decouples the creation logic for
different components from the client code. Each factory specializes in producing
components for a specific type of vehicle.
2. Scalability: Adding a new vehicle type requires creating a new concrete factory and
component classes without modifying existing code.
3. Flexibility: Changing the implementation of a component (e.g., upgrading car engines)
only affects the corresponding factory and component classes.
4. Consistency: Ensures that all components for a vehicle are compatible with each
other since they are created by the same factory.

d) Client Code Example


public class VehicleManufacturingClient {
public static void main(String[] args) {
VehicleFactory carFactory = new CarFactory();
assembleVehicle(carFactory);

VehicleFactory truckFactory = new TruckFactory();


assembleVehicle(truckFactory);

VehicleFactory motorcycleFactory = new MotorcycleFactory();


assembleVehicle(motorcycleFactory);
}

public static void assembleVehicle(VehicleFactory factory) {


Engine engine = factory.createEngine();
Wheels wheels = factory.createWheels();
Chassis chassis = factory.createChassis();
engine.assemble();
wheels.assemble();
chassis.assemble();

System.out.println("Vehicle assembly complete!\n");


}
}

SAMPLEOUPUT:
6. Design a Logger Management System in Java that utilizes both the
Singleton and Factory design patterns. In this system: a) Singleton
Pattern: Ensure that there is only one instance of the LoggerManager
class throughout the application. This class will manage the logging
system and provide access to various types of loggers. b) Factory
Pattern: Implement a factory method within the LoggerManager that
creates and returns different types of loggers (e.g., FileLogger,
ConsoleLogger) based on the specified type. In your design: a) Describe
the role of the LoggerManager class and how the Singleton pattern is
applied to it. b) Explain how the Factory pattern is used within the
LoggerManager to create different types of loggers. c) Provide a code
implementation for the LoggerManager class, the Logger interface, and
at least two concrete implementations of loggers (e.g., FileLogger and

ConsoleLogger). d) Discuss how you would use this LoggerManager in a


real-world application to ensure efficient and consistent logging.

a) Role of the LoggerManager Class


The LoggerManager:
1. Singleton Pattern: Ensures that there is only one instance of LoggerManager
throughout the application, managing all logging operations and providing a centralized
access point for loggers.
2. Factory Pattern: Provides a method to create and return instances of various loggers
(FileLogger, ConsoleLogger) based on the required type.

b) Use of Factory Pattern in LoggerManager


The factory method in LoggerManager abstracts the creation of loggers, allowing the client to
request a logger by type. This keeps the creation logic centralized and simplifies the
addition of new logger types.

c)
Logger Interface and Concrete Loggers
public interface Logger {
void log(String message);
}

public class ConsoleLogger implements Logger {


public void log(String message) {
System.out.println("[ConsoleLogger]: " + message);
}
}

public class FileLogger implements Logger {

public void log(String message) {


System.out.println("[FileLogger]: " + message);
}
}

Singleton LoggerManager with Factory Method


public class LoggerManager {

private static LoggerManager instance;

private LoggerManager() {}

public static synchronized LoggerManager getInstance() {


if (instance == null) {
instance = new LoggerManager();
}
return instance;
}

public Logger getLogger(String loggerType) {


switch (loggerType.toLowerCase()) {
case "console":
return new ConsoleLogger();
case "file":
return new FileLogger();
default:
throw new IllegalArgumentException("Invalid logger type: " + loggerType);
}
}
}

Example Usage of LoggerManager in an Application


public class LoggerDemo {
public static void main(String[] args) {
LoggerManager loggerManager = LoggerManager.getInstance();

Logger consoleLogger = loggerManager.getLogger("console");


consoleLogger.log("This is a console log message.");

Logger fileLogger = loggerManager.getLogger("file");


fileLogger.log("This is a file log message.");

LoggerManager anotherLoggerManager = LoggerManager.getInstance();


System.out.println("Are both instances the same? " + (loggerManager ==
anotherLoggerManager));
}
}

d) Using LoggerManager in a Real-World Application


1. Centralized Logging:
o With LoggerManager, logging is managed centrally, ensuring consistent
formatting and behavior across all loggers.
2. Efficient Resource Utilization:
o By ensuring a single instance of LoggerManager, the system avoids redundant
resource allocation for managing loggers.
3. Dynamic Logger Selection:
o The factory method allows the application to select loggers dynamically at
runtime (e.g., using environment configurations to choose between FileLogger
for production and ConsoleLogger for debugging).
4. Scalability:
o Adding new loggers (e.g., DatabaseLogger, RemoteLogger) is straightforward.
Simply implement the Logger interface and update the factory method.

Advantages of the Combined Pattern Design


1. Singleton:
o Ensures only one LoggerManager instance, reducing the risk of conflicting
configurations.
o Promotes efficient resource usage and consistent management.
2. Factory:
o Simplifies the addition of new logger types without altering existing client code.
o Centralizes the instantiation logic, adhering to the Single Responsibility
Principle.
SAMPLE OUTPUT:

You might also like