Дизайнерски шаблони в Java - Елегантност и Ефективност
- Теория
- Код
Дизайнерски Шаблони в Java
Дизайнерските шаблони са проверени решения на често срещани проблеми в софтуерното инженерство. В Java, те са от съществено значение за създаването на ефективен, чист и поддържаем код.
Структурни Шаблони
Структурните шаблони се фокусират върху начина, по който класове и обекти са съставени, за да създадат по-големи структури.
Примери:
- Adapter: Позволява на несъвместими интерфейси да работят заедно.
- Composite: Съчетава обекти в дървовидни структури, за да представят йерархия от части и цяло.
Създаващи Шаблони
Създаващите шаблони предоставят различни начини за създаване на обекти, като се стремят да се избегне директното инстанциране на обекти.
Примери:
- Singleton: Гарантира, че клас има само една инстанция и предоставя глобална точка за достъп до нея.
- Factory Method: Дефинира интерфейс за създаване на обект, но оставя наследниците да решат кой клас да инстанцират.
Поведенчески Шаблони
Поведенческите шаблони се отнасят до комуникацията между обекти и тяхното взаимодействие.
Примери:
- Observer: Позволява на обект да уведомява други обекти за промени в състоянието си.
- Strategy: Позволява дефинирането на семейство от алгоритми, енкапсулиране на всеки от тях и гарантира тяхната взаимозаменяемост.
Защо са важни дизайнерските шаблони?
- Повторно използване на код: Патерните са проверени решения, които могат да бъдат използвани отново и отново.
- Ефективност: Те помагат за намаляване на сложността на кода и улесняват поддръжката.
- Комуникация: Дизайнерските шаблони предоставят общ език между програмистите за обсъждане на решенията.
В следващите лекции, ще разгледаме всеки от тези шаблони по-подробно, с примери и тяхното приложение в Java.
Дизайнерски Шаблони в Java
1. Adapter (Адаптер)
Цел: Позволява на несъвместими интерфейси да работят заедно.
Пример:
// Целеви интерфейс
interface MediaPlayer {
void play(String audioType, String fileName);
}
// Съществуващ клас с различен интерфейс
class AdvancedMediaPlayer {
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: "+ fileName);
}
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: "+ fileName);
}
}
// Адаптер клас
class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType){
if(audioType.equalsIgnoreCase("vlc") ){
advancedMusicPlayer = new AdvancedMediaPlayer();
} else if (audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer = new AdvancedMediaPlayer();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")){
advancedMusicPlayer.playVlc(fileName);
} else if(audioType.equalsIgnoreCase("mp4")){
advancedMusicPlayer.playMp4(fileName);
}
}
}
// Клиентски клас
class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")){
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
2. Singleton (Единствена Инстанция)
Цел: Гарантира, че клас има само една инстанция и предоставя глобална точка за достъп до нея.
Пример:
class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. Factory Method (Фабричен Метод)
Цел: Дефинира интерфейс за създаване на обект, но оставя наследниците да решат кой клас да инстанцират.
Пример:
abstract class Shape {
abstract void draw();
}
class Rectangle extends Shape {
@Override
void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
class ShapeFactory {
public Shape getShape(String shapeType) {
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}
return null;
}
}
4. Observer (Наблюдател)
Цел: Позволява на обект да уведомява други обекти за промени в състоянието си.
Пример:
import java.util.ArrayList;
import java.util.List;
// Наблюдаемият клас
class Subject {
private List<Observer> observers = new ArrayList<Observer>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyAllObservers();
}
public void attach(Observer observer){
observers.add(observer);
}
public void notifyAllObservers(){
for (Observer observer : observers) {
observer.update();
}
}
}
// Наблюдател
abstract class Observer {
protected Subject subject;
public abstract void update();
}
class ConcreteObserver extends Observer
{
public ConcreteObserver(Subject subject){
this.subject = subject;
this.subject.attach(this);
}
@Override
public void update() {
System.out.println("State Changed: " + subject.getState());
}
}
5. Strategy (Стратегия)
Цел: Позволява дефинирането на семейство от алгоритми, енкапсулиране на всеки от тях и гарантира тяхната взаимозаменяемост.
Пример:
interface Strategy {
int doOperation(int num1, int num2);
}
class OperationAdd implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
class OperationSubtract implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2){
return strategy.doOperation(num1, num2);
}
}