MaterialEventPublisher.java
package com.university.bookstore.observer;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.university.bookstore.model.Material;
/**
* Concrete implementation of MaterialSubject that publishes material events to observers.
* Provides thread-safe event publishing and observer management.
*
* <p>This class demonstrates the Observer pattern by managing a list of observers
* and broadcasting events to all registered observers when material events occur.</p>
*
* @author Navid Mohaghegh
* @version 3.0
* @since 2024-09-15
*/
public class MaterialEventPublisher implements MaterialSubject {
private static final Logger LOGGER = LoggerFactory.getLogger(MaterialEventPublisher.class);
private final List<MaterialObserver> observers;
/**
* Creates a new material event publisher.
*/
public MaterialEventPublisher() {
// Use CopyOnWriteArrayList for thread safety
this.observers = new CopyOnWriteArrayList<>();
}
@Override
public void addObserver(MaterialObserver observer) {
if (observer == null) {
throw new IllegalArgumentException("Observer cannot be null");
}
if (!observers.contains(observer)) {
observers.add(observer);
observer.onAdded(this);
}
}
@Override
public boolean removeObserver(MaterialObserver observer) {
if (observer == null) {
return false;
}
boolean removed = observers.remove(observer);
if (removed) {
observer.onRemoved(this);
}
return removed;
}
@Override
public void notifyObservers(MaterialEvent event) {
if (event == null) {
throw new IllegalArgumentException("Event cannot be null");
}
for (MaterialObserver observer : observers) {
try {
observer.onEvent(event);
} catch (Exception e) {
// Log error but don't let one observer's failure affect others
LOGGER.error("Observer {} failed to handle event: {}",
observer.getObserverName(), event.getEventType(), e);
}
}
}
@Override
public int getObserverCount() {
return observers.size();
}
@Override
public boolean hasNoObservers() {
return observers.isEmpty();
}
@Override
public void clearObservers() {
// Notify observers they are being removed
for (MaterialObserver observer : observers) {
try {
observer.onRemoved(this);
} catch (Exception e) {
LOGGER.warn("Error notifying observer of removal", e);
}
}
observers.clear();
}
/**
* Publishes a material added event.
*
* @param material the material that was added
*/
public void publishMaterialAdded(Material material) {
notifyObservers(new MaterialAddedEvent(material));
}
/**
* Publishes a price changed event.
*
* @param material the material whose price changed
* @param oldPrice the previous price
* @param newPrice the new price
*/
public void publishPriceChanged(Material material, double oldPrice, double newPrice) {
notifyObservers(new PriceChangedEvent(material, oldPrice, newPrice));
}
/**
* Publishes a custom material event.
*
* @param event the event to publish
*/
public void publishEvent(MaterialEvent event) {
notifyObservers(event);
}
/**
* Gets a list of all registered observers.
*
* @return list of observers (defensive copy)
*/
public List<MaterialObserver> getObservers() {
return new ArrayList<>(observers);
}
/**
* Checks if a specific observer is registered.
*
* @param observer the observer to check
* @return true if the observer is registered
*/
public boolean hasObserver(MaterialObserver observer) {
return observers.contains(observer);
}
/**
* Gets observers of a specific type.
*
* @param observerType the type of observers to find
* @return list of observers of the specified type
*/
public <T extends MaterialObserver> List<T> getObserversOfType(Class<T> observerType) {
List<T> result = new ArrayList<>();
for (MaterialObserver observer : observers) {
if (observerType.isInstance(observer)) {
result.add(observerType.cast(observer));
}
}
return result;
}
@Override
public String toString() {
return String.format("MaterialEventPublisher[Observers=%d]", getObserverCount());
}
}