Budget Manager fragen zur Umsetzung

DrPils

Bekanntes Mitglied
Moin

Ich starte gerade als Übungsprojekt einen Budget Manager.
Starten soll alles als einfache Konsolen Anwendung, aber es soll aufjedenfall erweiterbar sein, so dass ich es später als GUI Programm umsetzen kann.

Es sollen transaktionen hinzugefügt werden können, welche in dem Account gespeichert werden und das Guthaben beeinflussen.

Hier mal eine Blaupause, dachte es ist besser als es in Worte zu formulieren.
class Account {
private int balance;
private List<Income> incomes;
private List<Purchase> purchases;
}


interface Transaction {
void transaction(Account account);
}

class Purchase implements Transaction {

private int amount;
private String description;
//private Category category;

@Override
public void transaction(Account account) {
//Die Ausgabe wird der transactions List hinzugefügt und die balance
// dementsprechend verringert
}
}

class Income implements Transaction {

private int amount;
private String description;
//private Category category;

@Override
public void transaction(Account account) {
//Die Einnahme wird der Incomes List hinzugefügt und die balance
// dementsprechend verringert
}
}
[/CODE]

Mein Problem ist jetzt, dass ich jeder Transaktion eine Kategorie hinzufügen möchte, also zb Lebensmittel, Miete, etc.
Die Transaktionen sollen nach dieser Kategorie gefiltert werden können. Leider habe ich keine Ahnung wie ich Kategorie umsetzen soll.
Enum fällt aus, da Kategorien hinzugefügt werden können. Außerdem sind mir die Klassen Income und Purchase ein Dorn im Auge,
da sie identische Attribute haben, wäre statt ein Interface eine Abstrakte Klasse evtl eher angebracht?

Danke schonmal!
 
Außerdem sind mir die Klassen Income und Purchase ein Dorn im Auge,
da sie identische Attribute haben, wäre statt ein Interface eine Abstrakte Klasse evtl eher angebracht?
Warum nicht einfach nur eine gemeinsame Klasse für Einnahmen und Ausgaben? 🙂

Leider habe ich keine Ahnung wie ich Kategorie umsetzen soll.
Enum fällt aus, da Kategorien hinzugefügt werden können.
Vielleicht einfach eine Klasse Category mit einem Namen als Attribut?
 
Warum nicht einfach nur eine gemeinsame Klasse für Einnahmen und Ausgaben? 🙂
Ich möchte sie in verschiedene Listen speichern und evtl später auch getrennt behandeln. Also zb Income kann immer nur positiv sein und Purchase immer nur negativ.

Vielleicht einfach eine Klasse Category mit einem Namen als Attribut?
Gute Frage 😀 aber dann könnte ich ja direkt Category als Attribut der Klasse Incom und Purchase deklarieren. Aber dann hätte ich meiner Meinung nach das Problem dass ich wenn ich sie in getrennten Listen packen möchte, ein unschönes Konstrukt von
Java:
if(purchase.category.equals("xxxx")){
    liste1.add(purchase)
}else if(purchase.category.equals("xxxy")){
    liste2.add(purchase)
}else if(...){
    ...
}....
hätte. Oder liege ich da falsch?
 
Ich möchte sie in verschiedene Listen speichern und evtl später auch getrennt behandeln. Also zb Income kann immer nur positiv sein und Purchase immer nur negativ.
Wenn du dafür wirklich zwei verschiedene Klassen brauchst, kannst du natürlich zusätzlich eine abstrakte Klasse einführen.
aber zumindest die getrennten Listen sind ja auch ohne extra Klassen möglich, nur halt nicht nach Typ getrennt.
Gute Frage 😀 aber dann könnte ich ja direkt Category als Attribut der Klasse Incom und Purchase deklarieren.
Theoretisch ja, allerdings hast du mit einer eigenen Klasse den Vorteil, das explizit ist, was es ist 🙂 eine Kategorie ist ja nicht einfach ein String.


Aber dann hätte ich meiner Meinung nach das Problem dass ich wenn ich sie in getrennten Listen packen möchte, ein unschönes Konstrukt
Das Konstrukt wäre ja unabhängig davon, wie Kategorien aussehen – es ließe sich aber unabhängig davon schöner lösen, ohne eigene if‘s
 
So ich habe jetzt mal ein bisschen daran rumgetüftelt. Insbesondere meine sortedTransactions() scheint mir eine einzige Katastrophe zu sein(class Account Zeile 67). Sie soll mir eine nested List zurückgeben deren innere Listen jeweils die Transaktionen der gleichen Kategorie beinhalten.
Dafür erstelle ich ein Hashset, durchlaufe meine transactions List und und füge die Category jeder Transaktion dem HashSet hinzu. Das mache ich da in einem Hashset ja jedes Element einzigartig ist. Nun durchlaufe ich das HashSet und erstelle für jedes Element eine neue Arraylist welche von der transactionsFilteredByCategory eine Liste der jeweiligen Kategorie bekommt. Diese Liste füge ich dann meiner nested List hinzu.
Ich sehe schon selbst dass das nicht sauber sein kann und wäre daher für Ratschläge dankbar.

[CODE lang="java" title="Account" highlight="67 - 76"]package budgetmanager.account;

import budgetmanager.transaction.Category;
import budgetmanager.transaction.Income;
import budgetmanager.transaction.Purchase;
import budgetmanager.transaction.Transaction;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public final class Account {

private final static Account instance = new Account();

private final List<Transaction> transactions;

private int balance;

private Account() {
transactions = new ArrayList<>();
}

public void doTransaction(Transaction transaction) {
if (transaction instanceof Purchase) {
balance -= transaction.getAmount();
} else {
balance += transaction.getAmount();
}
transactions.add(transaction);
}

public static Account getInstance() {
return instance;
}

public List<Income> incomesList() {
List<Income> incomes = new ArrayList<>();
for (Transaction t : transactions) {
if (t instanceof Income) {
incomes.add((Income) t);
}
}
return incomes;
}

public List<Purchase> purchasesList() {
List<Purchase> purchases = new ArrayList<>();
for (Transaction t : transactions) {
if (t instanceof Purchase) {
purchases.add((Purchase) t);
}
}
return purchases;
}

public List<Transaction> transactionsFilteredByCategory(Category category) {
List<Transaction> filtered = new ArrayList<>();
for (Transaction t : transactions) {
if (t.getCategory().equals(category)) {
filtered.add(t);
}
}
return filtered;
}

public List<List<Transaction>> sortedTransactions() {
HashSet<Category> categories = new HashSet<>();
transactions.forEach(t -> categories.add(t.getCategory()));
List<List<Transaction>> sorted = new ArrayList<>(categories.size());
for (Category category : categories) {
List<Transaction> t = transactionsFilteredByCategory(category);
sorted.add(t);
}
return sorted;
}
}

[/CODE]

[CODE lang="java" title="Category"]package budgetmanager.transaction;

import java.util.Objects;

public class Category {

private final String name;

public Category(String name) {
this.name = name;
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Category{");
sb.append("name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

Category category = (Category) o;

return Objects.equals(name, category.name);
}

@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
}
[/CODE]

[CODE lang="java" title="Income"]package budgetmanager.transaction;

public class Income extends Transaction {

public Income(String description, Category category, int amount) {
super(description, category, amount);
}

}



[/CODE]

[CODE lang="java" title="Purchase"]package budgetmanager.transaction;

public class Purchase extends Transaction {

public Purchase(String description, Category category, int amount) {
super(description, category, amount);
}

}
[/CODE]
[CODE lang="java" title="Purchase"]package budgetmanager.transaction;

import java.time.LocalDate;


public abstract class Transaction {

private final String description;
private final Category category;
private final int amount;
private final LocalDate date;


public Transaction(String description, Category category,
int amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Amount must be positive");
}
this.description = description;
this.category = category;
this.amount = amount;
this.date = LocalDate.now();
}


public String getDescription() {
return description;
}

public Category getCategory() {
return category;
}

public double getAmount() {
return amount;
}

public LocalDate getDate() {
return date;
}


@Override
public String toString() {
StringBuilder sb = new StringBuilder("Transaction{");
sb.append("description='").append(description).append('\'');
sb.append(", category=").append(category);
sb.append(", amount=").append(amount);
sb.append(", date=").append(date);
sb.append('}');
return sb.toString();
}
}
[/CODE]
 
Insbesondere meine sortedTransactions() scheint mir eine einzige Katastrophe zu sein(class Account Zeile 67). Sie soll mir eine nested List zurückgeben deren innere Listen jeweils die Transaktionen der gleichen Kategorie beinhalten.
Ich würde List<List<..>> generell immer als Katastrophe ansehen, aber ansonsten sieht die Methode gar nicht so schlimm aus, besser wäre eher sowas wie Map<Category, List<Transaction>>, je nachdem was du machen willst natürlich.

Und vorher solltest du dir die Frage stellen, ob die Methode überhaupt nötig ist, oder nicht schon der Account selbst ausreichend ist – von dem bekommst du ja jeweils alle Transactions zu einer Category 😉
 
Wieso würdest du sagen etwas wie List<List<...>> ist immer eine Katastrophe? Die Befürchtung hatte ich zwar auch schon, aber begründen konnte ich es nicht.
Ja der Sinn der Methode erschließt sich mir jetzt auch nicht mehr 😀
 
Was ist sowas denn? Die Bedeutung ist nicht leicht erkennbar und das ist ein Zeichen, dass die Lösung zu komplex gewählt wurde.

Ziel soll ja sein, dass Code sich selbst erklärt. Und dazu ist so ein Ausdruck nicht wirklich geeignet.

Ich möchte nicht ausschließen, dass es ggf. Situationen gibt, wo wirklich nur eine Liste von Listen das ist, was da gespeichert wird, aber das dürfte zumindest selten vorkommen.
 
Ich würde nur eine Klasse für Transaktionen verwenden und Ein- und Auszahlung als positiven oder negative Betrag berücksichtigen. Der Kontostand kann über eine Abwicklung aller Transaktionen ermittelt werden.
Java:
class Account
    List<Transaction> transactions;

    void applyTransaction(Transaction transaction) {
        transactions.add(transaction);
    }

    int getBalance() {
        int balance = 0;
        for(var transaction : transactions) {
            balance += transaction.getValue();
        }
       
        return balance;
    }
}

class Transaction {
    // Zeitstempel, Kommentar usw.
    private int value;
   
    int getValue() {
        return value;
    }
}

Die Kategorien könnten auch in einer separaten Klasse gespeichert werden.
Java:
class Categories {
    private Map<Category, List<Transaction>> categories;
   
    public void add(Transaction transaction, Category category) {
       
        List<Transaction> transactions = categories.get(category);
       
        if (transactions == null) {
            transactions = new List<Transaction>();
        }
       
        transactions.add(transaction);
        categories.put(category, transactions);
    }
   
    public List<Transaction> getAll(Category category) {
        return categories.get(category);
    }
}
 

Neue Themen


Zurück
Oben