-
Notifications
You must be signed in to change notification settings - Fork 0
Java Design Patterns: A Comprehensive Overview
Design patterns in Java are essential solutions to common software design problems, helping developers write efficient and maintainable code. This comprehensive overview explores various Java Design Patterns, including creational, structural, and behavioral patterns, each serving a unique purpose in software architecture. By implementing these patterns, such as Singleton, Factory, and Observer, developers can enhance their applications' flexibility and robustness. For those looking to deepen their understanding, resources like tpointtech offer detailed explanations and examples. Understanding and utilizing design patterns in Java is crucial for building scalable and high-quality software.
Creational patterns focus on object creation mechanisms, aiming to create objects in a manner suitable to the situation. These patterns help make a system independent of how its objects are created, composed, and represented.
Ensures a class has only one instance and provides a global point of access to it. This pattern is useful in scenarios where a single object is needed to coordinate actions across the system.
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Defines an interface for creating an object, but lets subclasses alter the type of objects that will be created. This pattern promotes loose coupling by eliminating the need to bind application-specific classes into the code.
public interface Shape {
void draw();
}
public class Circle implements Shape {
public void draw() {
System.out.println("Drawing Circle");
}
}
public class ShapeFactory {
public Shape getShape(String shapeType) {
if (shapeType.equals("CIRCLE")) {
return new Circle();
}
return null;
}
}
Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
public class Pizza { private String dough; private String sauce; private String topping;
public static class Builder {
private String dough;
private String sauce;
private String topping;
public Builder setDough(String dough) {
this.dough = dough;
return this;
}
public Builder setSauce(String sauce) {
this.sauce = sauce;
return this;
}
public Builder setTopping(String topping) {
this.topping = topping;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
private Pizza(Builder builder) {
dough = builder.dough;
sauce = builder.sauce;
topping = builder.topping;
}
}
Structural patterns deal with object composition, defining ways to compose objects to form larger structures while keeping these structures flexible and efficient.
Allows incompatible interfaces to work together. This pattern involves a single class which is responsible to join functionalities of independent or incompatible interfaces.
public interface MediaPlayer { void play(String audioType, String fileName); }
public class AudioPlayer implements MediaPlayer {
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("mp3")){
System.out.println("Playing mp3 file. Name: " + fileName);
}
}
}
public class MediaAdapter implements MediaPlayer { AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new Mp4Player();
}
}
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
}
else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
Composes objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly.
public interface Employee { void showEmployeeDetails(); }
public class Developer implements Employee { private String name; private long empId;
public Developer(String name, long empId) {
this.name = name;
this.empId = empId;
}
public void showEmployeeDetails() {
System.out.println(empId + " " + name);
}
}
public class Manager implements Employee { private String name; private long empId; List employees = new ArrayList();
public Manager(String name, long empId) {
this.name = name;
this.empId = empId;
}
public void addEmployee(Employee emp) {
employees.add(emp);
}
public void showEmployeeDetails() {
System.out.println(empId + " " + name);
for(Employee emp : employees) {
emp.showEmployeeDetails();
}
}
}
Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects. They describe not just the structure of relationships but also the patterns of communication.
Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
public interface Observer { void update(); }
public interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(); }
public class NewsAgency implements Subject { private List observers = new ArrayList<>(); private String news;
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
public void setNews(String news) {
this.news = news;
notifyObservers();
}
}
public class NewsChannel implements Observer { private String news;
public void update(String news) {
this.news = news;
System.out.println("News Channel received update: " + news);
}
}
Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
public interface PaymentStrategy { void pay(int amount); }
public class CreditCardStrategy implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid with credit card: " + amount); } }
public class PaypalStrategy implements PaymentStrategy { public void pay(int amount) { System.out.println("Paid with PayPal: " + amount); } }
public class ShoppingCart { private List items; private PaymentStrategy paymentStrategy;
public ShoppingCart(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void addItem(Item item) {
items.add(item);
}
public void pay() {
int amount = items.stream().mapToInt(Item::getPrice).sum();
paymentStrategy.pay(amount);
}
}
Mastering Design Patterns in Java is crucial for developing efficient, maintainable, and scalable software. These patterns, categorized into creational, structural, and behavioral, provide standardized solutions to common design problems, enhancing code readability and reusability. Resources like tpointtech offer comprehensive guides and examples, making it easier for developers to learn and implement these patterns effectively. By integrating these design principles, developers can significantly improve the robustness and flexibility of their Java applications, ensuring long-term success and adaptability in their software projects.
For More Info- tpointtech