We have:
- An abstract class with common methods.
- Three concrete classes inheriting from the abstract class and implementing an interface.
- A manager class that invokes the
logmethod based on the type of class passed to it.
We aim to:
- Write the required
iflogic. - Improve the architecture by eliminating tight coupling and making the design SOLID.
// Interface
public interface Logger {
void log();
}
// Abstract Class
public abstract class BaseLogger implements Logger {
protected String message;
public BaseLogger(String message) {
this.message = message;
}
}
// Concrete Classes
public class FileLogger extends BaseLogger {
public FileLogger(String message) {
super(message);
}
@Override
public void log() {
System.out.println("FileLogger: " + message);
}
}
public class DatabaseLogger extends BaseLogger {
public DatabaseLogger(String message) {
super(message);
}
@Override
public void log() {
System.out.println("DatabaseLogger: " + message);
}
}
public class ConsoleLogger extends BaseLogger {
public ConsoleLogger(String message) {
super(message);
}
@Override
public void log() {
System.out.println("ConsoleLogger: " + message);
}
}
// Manager Class
public class Manager {
private final BaseLogger logger;
public Manager(BaseLogger logger) {
this.logger = logger;
}
public void processLogging() {
if (logger instanceof FileLogger) {
logger.log();
} else if (logger instanceof DatabaseLogger) {
logger.log();
} else if (logger instanceof ConsoleLogger) {
logger.log();
} else {
throw new IllegalArgumentException("Unsupported logger type");
}
}
}
// Main Class
public class Main {
public static void main(String[] args) {
Manager manager = new Manager(new FileLogger("File logging message"));
manager.processLogging();
manager = new Manager(new DatabaseLogger("Database logging message"));
manager.processLogging();
manager = new Manager(new ConsoleLogger("Console logging message"));
manager.processLogging();
}
}-
Violation of Open/Closed Principle
- The
Managerclass needs modification whenever a new type of logger is added, as it relies onifconditions to determine behavior.
- The
-
Violation of Single Responsibility Principle
- The
Managerclass is responsible for determining the logger type and invoking thelogmethod, combining two concerns.
- The
-
Tight Coupling
Manageris tightly coupled with specific logger implementations.
We use Dependency Injection and Polymorphism to remove if conditions and make the design flexible and extensible.
// Interface
public interface Logger {
void log();
}
// Abstract Class
public abstract class BaseLogger implements Logger {
protected String message;
public BaseLogger(String message) {
this.message = message;
}
}
// Concrete Classes
public class FileLogger extends BaseLogger {
public FileLogger(String message) {
super(message);
}
@Override
public void log() {
System.out.println("FileLogger: " + message);
}
}
public class DatabaseLogger extends BaseLogger {
public DatabaseLogger(String message) {
super(message);
}
@Override
public void log() {
System.out.println("DatabaseLogger: " + message);
}
}
public class ConsoleLogger extends BaseLogger {
public ConsoleLogger(String message) {
super(message);
}
@Override
public void log() {
System.out.println("ConsoleLogger: " + message);
}
}
// Manager Class
public class Manager {
private final Logger logger;
public Manager(Logger logger) {
this.logger = logger;
}
public void processLogging() {
logger.log(); // Polymorphism handles the method call
}
}
// Main Class
public class Main {
public static void main(String[] args) {
Manager fileManager = new Manager(new FileLogger("File logging message"));
fileManager.processLogging();
Manager databaseManager = new Manager(new DatabaseLogger("Database logging message"));
databaseManager.processLogging();
Manager consoleManager = new Manager(new ConsoleLogger("Console logging message"));
consoleManager.processLogging();
}
}-
Open/Closed Principle
- The
Managerclass is open for extension but closed for modification. Adding a new logger (e.g.,CloudLogger) requires no changes to theManager.
- The
-
Single Responsibility Principle
- The
Managerclass is responsible only for invoking thelogmethod. The logger type is determined outside theManager.
- The
-
Liskov Substitution Principle
- Any new
Loggerimplementation can be substituted without changing the behavior of theManager.
- Any new
-
Dependency Inversion Principle
Managerdepends on theLoggerabstraction, not on concrete implementations likeFileLoggerorDatabaseLogger.
-
Interface Segregation Principle
- The
Loggerinterface is specific to logging, ensuring no unnecessary methods are forced on classes.
- The
- Scalability: New logger types can be added without modifying existing code.
- Readability: The code is cleaner and easier to understand.
- Flexibility: Swapping logger implementations becomes simple.
- Maintainability: The separation of concerns ensures each class focuses on a single responsibility.
Using SOLID principles in your architecture eliminates tight coupling, makes the code extensible, and reduces maintenance overhead.