Skip to content

Commit

Permalink
Merge pull request #17 from solvena/16-add-observer
Browse files Browse the repository at this point in the history
Add observer
  • Loading branch information
solvena authored May 24, 2026
2 parents dcd5041 + b6c27e2 commit 9225c8c
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 78 deletions.
34 changes: 30 additions & 4 deletions src/main/java/Model/Exchange.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public class Exchange {
private final Map<String, Stock> stockMap;
private final Random random;

// Registered observers
private final List<ExchangeObserver> observers = new ArrayList<>();

public Exchange(String name, List<Stock> stocks) {
this.name = name;
this.week = 1;
Expand All @@ -22,9 +25,26 @@ public Exchange(String name, List<Stock> stocks) {
for (Stock stock : stocks) {
stockMap.put(stock.getSymbol(), stock);
}
}

// ---- Observer ----

public void addObserver(ExchangeObserver observer) {
if (observer != null && !observers.contains(observer)) {
observers.add(observer);
}
}

public void removeObserver(ExchangeObserver observer) {
observers.remove(observer);
}


private void notifyObservers() {
for (ExchangeObserver observer : observers) {
observer.onExchangeUpdated(this);
}
}

public String getName() {
return name;
}
Expand Down Expand Up @@ -57,7 +77,7 @@ public List<Stock> findStocks(String searchTerm) {

public Transaction buy(String symbol, BigDecimal quantity, Player player) {
Stock stock = getStock(symbol);

// unngå nullpointerexception
if (stock == null) {
return null;
Expand All @@ -71,7 +91,9 @@ public Transaction buy(String symbol, BigDecimal quantity, Player player) {

// committer til player
purchase.commit(player);


notifyObservers();

return purchase;
}

Expand All @@ -86,7 +108,9 @@ public Transaction sell(Share share, Player player) {

// commiter til player
sale.commit(player);


notifyObservers();

return sale;
}

Expand All @@ -108,6 +132,8 @@ public void advance() {
stock.addNewSalesPrice(newPrice);
}
}

notifyObservers();
}

public List<Stock> getGainers(int limit) { // viser "vinnerne"
Expand Down
16 changes: 16 additions & 0 deletions src/main/java/Model/ExchangeObserver.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package Model;

/**
* Observer interface for the Exchange subject.
* Implement this interface to receive notifications whenever the exchange
* state changes (week advances, a trade is committed).
*/
public interface ExchangeObserver {

/**
* Called by the Exchange after its state has changed.
*
* @param exchange the Exchange that triggered the notification
*/
void onExchangeUpdated(Exchange exchange);
}
144 changes: 70 additions & 74 deletions src/main/java/View/MainGameScene.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@
import java.math.BigDecimal;
import java.util.List;

public class MainGameScene {
/**
* Main game UI. Implements ExchangeObserver so the view automatically refreshes
* whenever the Exchange notifies it of a state change (week advance or trade).
*/
public class MainGameScene implements ExchangeObserver {

private Scene scene;
private Exchange exchange;
private Player player;
private Runnable onExit;
private Label statusLabel;
// References to UI components for refreshing

// References to UI components that need refreshing
private TableView<PortfolioRow> portfolioTable;
private ListView<Share> holdingsList;
private TableView<HistoryRow> historyTable;
Expand All @@ -29,9 +34,23 @@ public MainGameScene(Exchange exchange, Player player, Runnable onExit) {
this.exchange = exchange;
this.player = player;
this.onExit = onExit;

// Register this view as an observer of the exchange
this.exchange.addObserver(this);

this.scene = createScene();
}

/**
* Called automatically by Exchange whenever its state changes.
* Refreshes all UI elements to reflect the latest data.
*/
@Override
public void onExchangeUpdated(Exchange exchange) {
updateStatus();
refreshAllUI();
}

private Scene createScene() {
VBox root = new VBox(0);

Expand All @@ -49,12 +68,13 @@ private Scene createScene() {

Button nextWeekBtn = new Button("Next week");
nextWeekBtn.getStyleClass().addAll("action-button");
nextWeekBtn.setOnAction(e -> advance());
nextWeekBtn.setOnAction(e -> exchange.advance()); // observer handles the UI update

Button exitBtn = new Button("Exit");
exitBtn.getStyleClass().add("exit-button");
exitBtn.setOnAction(e -> {
if (confirm("Exit Game?", "Final Net Worth: $" + formatMoney(getNetWorth()))) {
exchange.removeObserver(this); // clean up before closing
onExit.run();
}
});
Expand Down Expand Up @@ -156,11 +176,9 @@ private VBox createPortfolioPanel() {
alert("Error", "Select a holding to sell.");
return;
}
Transaction trans = exchange.sell(selected.s, player);
Transaction trans = exchange.sell(selected.s, player); // observer fires refresh
if (trans != null && trans.isCommitted()) {
showConfirmation("Sale successful", trans);
updateStatus();
refreshAllUI();
} else {
alert("Failed", "Could not complete the sale.");
}
Expand Down Expand Up @@ -226,14 +244,12 @@ private VBox createBuyTab() {
return;
}
BigDecimal qty = new BigDecimal(qtyField.getText());
Transaction trans = exchange.buy(s.getSymbol(), qty, player);
Transaction trans = exchange.buy(s.getSymbol(), qty, player); // observer fires refresh
if (trans != null && trans.isCommitted()) {
showConfirmation("Purchase successful", trans);
stockField.clear();
qtyField.clear();
infoLabel.setText("");
updateStatus();
refreshAllUI();
} else {
alert("Failed", "Insufficient funds or error");
}
Expand All @@ -247,62 +263,50 @@ private VBox createBuyTab() {
}

private VBox createSellTab() {
VBox box = new VBox(10);
box.getStyleClass().add("content-area");

Label heading = new Label("Your Holdings:");
VBox box = new VBox(10);
box.getStyleClass().add("content-area");

holdingsList = new ListView<>();
holdingsList.setPrefHeight(400);
Label heading = new Label("Your Holdings:");

holdingsList.setCellFactory(lv -> new ListCell<Share>() {
@Override
protected void updateItem(Share s, boolean empty) {
super.updateItem(s, empty);
holdingsList = new ListView<>();
holdingsList.setPrefHeight(400);

if (empty || s == null) {
setText(null);
} else {
setText(
s.getStock().getSymbol() + " - " +
s.getQuantity() + " @ $" +
formatMoney(s.getStock().getSalesPrice())
);
holdingsList.setCellFactory(lv -> new ListCell<Share>() {
@Override
protected void updateItem(Share s, boolean empty) {
super.updateItem(s, empty);
if (empty || s == null) {
setText(null);
} else {
setText(
s.getStock().getSymbol() + " - " +
s.getQuantity() + " @ $" +
formatMoney(s.getStock().getSalesPrice())
);
}
}
}
});

updateHoldingsList(holdingsList);

Button sellBtn = new Button("Sell Selected");

sellBtn.setOnAction(e -> {
Share selected = holdingsList.getSelectionModel().getSelectedItem();

if (selected == null) {
alert("Error", "Select a holding to sell");
return;
}

Transaction trans = exchange.sell(selected, player);

if (trans != null && trans.isCommitted()) {
showConfirmation("Sale successful", trans);
refreshAllUI();
} else {
alert("Failed", "Could not complete sale");
}
});
});

box.getChildren().addAll(
heading,
holdingsList,
sellBtn
);
updateHoldingsList(holdingsList);

return box;
}
Button sellBtn = new Button("Sell Selected");
sellBtn.setOnAction(e -> {
Share selected = holdingsList.getSelectionModel().getSelectedItem();
if (selected == null) {
alert("Error", "Select a holding to sell");
return;
}
Transaction trans = exchange.sell(selected, player); // observer fires refresh
if (trans != null && trans.isCommitted()) {
showConfirmation("Sale successful", trans);
} else {
alert("Failed", "Could not complete sale");
}
});

box.getChildren().addAll(heading, holdingsList, sellBtn);
return box;
}

private VBox createHistoryPanel() {
VBox panel = new VBox(10);
Expand All @@ -317,7 +321,8 @@ private VBox createHistoryPanel() {

weekFilterCombo.setOnAction(e -> updateHistory(
historyTable,
weekFilterCombo.getValue() == null || weekFilterCombo.getValue() == 0 ? null : weekFilterCombo.getValue()
weekFilterCombo.getValue() == null || weekFilterCombo.getValue() == 0
? null : weekFilterCombo.getValue()
));

HBox filterRow = new HBox(8);
Expand Down Expand Up @@ -394,13 +399,8 @@ private void updatePortfolio(TableView<PortfolioRow> table) {
}

private void updateHoldingsList(ListView<Share> list) {
ObservableList<Share> items =
FXCollections.observableArrayList();

items.addAll(
player.getPortfolio().getShares()
);

ObservableList<Share> items = FXCollections.observableArrayList();
items.addAll(player.getPortfolio().getShares());
list.setItems(items);
}

Expand Down Expand Up @@ -457,19 +457,15 @@ private void refreshAllUI() {
updateHoldingsList(holdingsList);
}
if (historyTable != null) {
updateHistory(historyTable, weekFilterCombo != null && weekFilterCombo.getValue() != null ? weekFilterCombo.getValue() : null);
updateHistory(historyTable,
weekFilterCombo != null && weekFilterCombo.getValue() != null
? weekFilterCombo.getValue() : null);
if (weekFilterCombo != null) {
updateWeekCombo(weekFilterCombo);
}
}
}

private void advance() {
exchange.advance();
updateStatus();
refreshAllUI();
}

private BigDecimal getNetWorth() {
return player.getMoney().add(player.getPortfolio().getNetWorth());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Controller\StockFileHandler.java
C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Exchange.java
C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\ExchangeObserver.java
C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Player.java
C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Portfolio.java
C:\Users\elisa\Downloads\progdel1\Programmering2_mappe_v26\src\main\java\Model\Purchase.java
Expand Down

0 comments on commit 9225c8c

Please sign in to comment.