Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implemented fruit shop program logic using solid principles #1292

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added finalReport.csv
Empty file.
9 changes: 0 additions & 9 deletions src/main/java/core/basesyntax/HelloWorld.java

This file was deleted.

61 changes: 61 additions & 0 deletions src/main/java/core/basesyntax/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package core.basesyntax;

import db.Storage;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import model.FruitTransaction;
import service.DataConverter;
import service.FileReader;
import service.ReportGenerator;
import service.ShopService;
import service.impl.DataConverterImpl;
import service.impl.FileReaderImpl;
import service.impl.FileWriterImpl;
import service.impl.OperationStrategyImpl;
import service.impl.ReportGeneratorImpl;
import service.impl.ShopServiceImpl;
import service.operation.BalanceOperation;
import service.operation.OperationHandler;
import service.operation.PurchaseOperation;
import service.operation.ReturnOperation;
import service.operation.SupplyOperation;
import strategy.OperationStrategy;

public class Main {
public static void main(String[] args) {
List<String> inputReport = null;
try {
FileReader fileReader = new FileReaderImpl("reportToRead.csv");
inputReport = fileReader.read("reportToRead.csv");
} catch (FileNotFoundException e) {
throw new RuntimeException("File not found: reportToRead.csv", e);
}

DataConverter dataConverter = new DataConverterImpl();

Map<FruitTransaction.Operation, OperationHandler> operationHandlers = new HashMap<>();
operationHandlers.put(FruitTransaction.Operation.BALANCE, new BalanceOperation());
operationHandlers.put(FruitTransaction.Operation.PURCHASE, new PurchaseOperation());
operationHandlers.put(FruitTransaction.Operation.RETURN, new ReturnOperation());
operationHandlers.put(FruitTransaction.Operation.SUPPLY, new SupplyOperation());
OperationStrategy operationStrategy = new OperationStrategyImpl(operationHandlers);

List<FruitTransaction> transactions = dataConverter.convertToTransaction(inputReport);
ShopService shopService = new ShopServiceImpl(operationStrategy);
shopService.process(transactions);

Storage storage = new Storage();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct instantiation of Storage might expose its internal state. Consider using a method to access its data to maintain encapsulation.

ReportGenerator reportGenerator = new ReportGeneratorImpl(storage);
String resultingReport = reportGenerator.getReport();
try {
FileWriter fileWriter = new FileWriterImpl("finalReport.csv");
fileWriter.write(resultingReport);
} catch (IOException e) {
throw new RuntimeException("Unable to create FileWriter for finalReport.csv", e);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same

}
}
27 changes: 27 additions & 0 deletions src/main/java/db/Storage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package db;

import java.util.HashMap;
import java.util.Map;

public class Storage {
private static final Map<String, Integer> fruits = new HashMap<>();

public static void add(String fruit, Integer amount) {
fruits.put(fruit, fruits.getOrDefault(fruit, 0) + amount);
}

public static void remove(String fruit, Integer amount) {
if (!fruits.containsKey(fruit) || fruits.get(fruit) < amount) {
throw new IllegalArgumentException("Not enough " + fruit + " in storage.");
}
fruits.put(fruit, fruits.get(fruit) - amount);
}

public static int getAmount(String fruit) {
return fruits.getOrDefault(fruit, 0);
}

public Map<String, Integer> getAllFruits() {
return new HashMap<>(fruits);
Comment on lines +24 to +25

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning a new HashMap in getAllFruits is a good practice for encapsulation, as it prevents external modification of the internal map.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix comment

}
}
Empty file.
9 changes: 9 additions & 0 deletions src/main/java/db/reportToRead.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type,fruit,quantity
b,banana,20
b,apple,100
s,banana,100
p,banana,13
r,apple,10
p,apple,20
p,banana,5
s,banana,50
48 changes: 48 additions & 0 deletions src/main/java/model/FruitTransaction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package model;

public class FruitTransaction {
private Operation operation;
private String fruit;
private int quantity;

public enum Operation {
BALANCE("b"),
SUPPLY("s"),
PURCHASE("p"),
RETURN("r");

private String code;

Operation(String code) {
this.code = code;
}

public String getCode() {
return code;
}
}

public Operation getOperation() {
return operation;
}

public void setOperation(Operation operation) {
this.operation = operation;
}

public String getFruit() {
return fruit;
}

public void setFruit(String fruit) {
this.fruit = fruit;
}

public int getQuantity() {
return quantity;
}

public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
8 changes: 8 additions & 0 deletions src/main/java/service/DataConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package service;

import java.util.List;
import model.FruitTransaction;

public interface DataConverter {
public List<FruitTransaction> convertToTransaction(List<String> reportLines);
}
11 changes: 11 additions & 0 deletions src/main/java/service/FileReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package service;

import java.io.FileNotFoundException;
import java.util.List;
import model.FruitTransaction;

public interface FileReader {
List<String> read(String fileName) throws FileNotFoundException;

FruitTransaction parseCsvRow(String csvRow);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parseCsvRow method might not belong in the FileReader interface, as it deals with parsing rather than reading files. Consider moving this method to a more appropriate class or interface, such as DataConverter.

}
5 changes: 5 additions & 0 deletions src/main/java/service/ReportGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package service;

public interface ReportGenerator {
String getReport();
}
8 changes: 8 additions & 0 deletions src/main/java/service/ShopService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package service;

import java.util.List;
import model.FruitTransaction;

public interface ShopService {
void process(List<FruitTransaction> fruitTransactionList);
}
37 changes: 37 additions & 0 deletions src/main/java/service/impl/DataConverterImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package service.impl;

import java.util.ArrayList;
import java.util.List;
import model.FruitTransaction;
import service.DataConverter;

public class DataConverterImpl implements DataConverter {
@Override
public List<FruitTransaction> convertToTransaction(List<String> report) {
List<FruitTransaction> fruitTransactionList = new ArrayList<>();
for (String line : report) {
FruitTransaction fruitTransaction = getFromCsvRow(line);
fruitTransactionList.add(fruitTransaction);
}
return fruitTransactionList;
}

private FruitTransaction getFromCsvRow(String row) {
String[] fields = row.split(",");
FruitTransaction fruitTransaction = new FruitTransaction();
fruitTransaction.setOperation(getOperationFromCode(fields[0]));
fruitTransaction.setFruit(fields[1]);
fruitTransaction.setQuantity(Integer.parseInt(fields[2]));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parsing fields[2] to an integer can throw a NumberFormatException if the input is not a valid integer. Consider adding a try-catch block to handle this exception and provide a more informative error message.

return fruitTransaction;
}

private FruitTransaction.Operation getOperationFromCode(String code) {
for (FruitTransaction.Operation operation : FruitTransaction.Operation.values()) {
if (operation.getCode().equals(code)) {
return operation;
}
}
throw new IllegalArgumentException("Unknown operation: " + code);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider logging the error or adding a comment to explain why an IllegalArgumentException is thrown for an unknown operation code. This can help with debugging and understanding the code's behavior.

}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

leave only one empty line

45 changes: 45 additions & 0 deletions src/main/java/service/impl/FileReaderImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package service.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import model.FruitTransaction;
import service.FileReader;

public class FileReaderImpl implements FileReader {
private final String filePath;

public FileReaderImpl(String filePath) {
this.filePath = filePath;
}

@Override
public List<String> read(String fileName) throws FileNotFoundException {
List<String> data = new ArrayList<>();
try (Scanner scanner = new Scanner(new File(filePath))) {
if (scanner.hasNext()) {
scanner.nextLine();
}
while (scanner.hasNext()) {
data.add(scanner.nextLine());
}
}
return data;
}

@Override
public FruitTransaction parseCsvRow(String csvRow) {
String[] fields = csvRow.split(",");
FruitTransaction fruitTransaction = new FruitTransaction();
fruitTransaction.setOperation(FruitTransaction.Operation.valueOf(fields[0]));
fruitTransaction.setFruit(fields[1]);
try {
fruitTransaction.setQuantity(Integer.parseInt(fields[2]));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid quantity format: " + fields[2], e);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message for Invalid quantity format is informative, but consider including the row number or more context to aid in debugging when this exception is thrown.

}
return fruitTransaction;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parseCsvRow method is responsible for parsing CSV rows into FruitTransaction objects, which is not directly related to file reading. Consider moving this method to a class that handles data conversion, such as DataConverterImpl, to improve separation of concerns.

}
}
10 changes: 10 additions & 0 deletions src/main/java/service/impl/FileWriterImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package service.impl;

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterImpl extends FileWriter {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add writing logic

public FileWriterImpl(String fileName) throws IOException {
super(fileName);
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor in FileWriterImpl is redundant as it doesn't add any new functionality beyond what FileWriter already provides. Consider removing it unless you plan to extend its functionality.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constructor in FileWriterImpl is redundant as it does not add any new functionality beyond calling the superclass constructor. Consider removing it unless you plan to add additional functionality.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same

}
20 changes: 20 additions & 0 deletions src/main/java/service/impl/OperationStrategyImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package service.impl;

import java.util.Map;
import model.FruitTransaction;
import service.operation.OperationHandler;
import strategy.OperationStrategy;

public class OperationStrategyImpl implements OperationStrategy {
private Map<FruitTransaction.Operation, OperationHandler> operationHandlerMap;

public OperationStrategyImpl(Map<FruitTransaction.Operation,
OperationHandler> operationHandlerMap) {
this.operationHandlerMap = operationHandlerMap;
}

@Override
public OperationHandler get(FruitTransaction.Operation operationType) {
return operationHandlerMap.get(operationType);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The get method may return null if the operation type is not found in the map. Consider handling this case by returning a default handler, throwing an exception, or providing a meaningful error message to avoid potential NullPointerException.

}
}
26 changes: 26 additions & 0 deletions src/main/java/service/impl/ReportGeneratorImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package service.impl;

import db.Storage;
import java.util.Map;
import service.ReportGenerator;

public class ReportGeneratorImpl implements ReportGenerator {
private final Storage storage;

public ReportGeneratorImpl(Storage storage) {
this.storage = storage;
}

@Override
public String getReport() {
StringBuilder report = new StringBuilder();
Map<String, Integer> fruits = storage.getAllFruits();

report.append("fruit,amount\n");
for (Map.Entry<String, Integer> entry : fruits.entrySet()) {
report.append(entry.getKey()).append(",").append(entry.getValue()).append("\n");
}

return report.toString();
}
}
23 changes: 23 additions & 0 deletions src/main/java/service/impl/ShopServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package service.impl;

import java.util.List;
import model.FruitTransaction;
import service.ShopService;
import service.operation.OperationHandler;
import strategy.OperationStrategy;

public class ShopServiceImpl implements ShopService {
private final OperationStrategy operationStrategy;

public ShopServiceImpl(OperationStrategy operationStrategy) {
this.operationStrategy = operationStrategy;
}

@Override
public void process(List<FruitTransaction> fruitTransactionList) {
for (FruitTransaction transaction : fruitTransactionList) {
OperationHandler handler = operationStrategy.get(transaction.getOperation());
handler.performOperation(transaction);
Comment on lines +19 to +20

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The operationStrategy.get method may return null if no handler is found for a given operation. Consider adding a null check and handling this case to prevent potential NullPointerException.

}
}
}
11 changes: 11 additions & 0 deletions src/main/java/service/operation/BalanceOperation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package service.operation;

import db.Storage;
import model.FruitTransaction;

public class BalanceOperation implements OperationHandler {
@Override
public void performOperation(FruitTransaction fruitTransaction) {
Storage.add(fruitTransaction.getFruit(), fruitTransaction.getQuantity());
}
}
7 changes: 7 additions & 0 deletions src/main/java/service/operation/OperationHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package service.operation;

import model.FruitTransaction;

public interface OperationHandler {
void performOperation(FruitTransaction fruitTransaction);
}
11 changes: 11 additions & 0 deletions src/main/java/service/operation/PurchaseOperation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package service.operation;

import db.Storage;
import model.FruitTransaction;

public class PurchaseOperation implements OperationHandler {
@Override
public void performOperation(FruitTransaction fruitTransaction) {
Storage.remove(fruitTransaction.getFruit(), fruitTransaction.getQuantity());
}
}
Loading