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 all 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
9 changes: 0 additions & 9 deletions src/main/java/core/basesyntax/HelloWorld.java

This file was deleted.

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

import db.Storage;
import java.io.FileNotFoundException;
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 {
private static final String REPORT_TO_READ_FILE = "src/main/resources/reportToRead.csv";
private static final String FINAL_REPORT_FILE = "src/main/resources/finalReport.csv";

public static void main(String[] args) throws FileNotFoundException {
FileReader fileReader = new FileReaderImpl();
List<String> inputReport = fileReader.read(REPORT_TO_READ_FILE);

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();

FileWriterImpl fileWriter = new FileWriterImpl();
fileWriter.write(FINAL_REPORT_FILE, resultingReport);
}
}
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

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

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

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;
}

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 static 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);
}
}
}
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 {
List<FruitTransaction> convertToTransaction(List<String> reportLines);
}
8 changes: 8 additions & 0 deletions src/main/java/service/FileReader.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package service;

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

public interface FileReader {
List<String> read(String fileName) throws FileNotFoundException;
}
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);
}
32 changes: 32 additions & 0 deletions src/main/java/service/impl/DataConverterImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package service.impl;

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

public class DataConverterImpl implements DataConverter {
private static final String COMMA_SEPARATOR = ",";
private static final int OPERATION_PARAMETER = 0;
private static final int FRUIT_PARAMETER = 1;
private static final int QUANTITY_PARAMETER = 2;

@Override
public List<FruitTransaction> convertToTransaction(List<String> report) {
List<FruitTransaction> fruitTransactionList = new ArrayList<>();
for (int i = 1; i < report.size(); i++) {
FruitTransaction fruitTransaction = getFromCsvRow(report.get(i));
fruitTransactionList.add(fruitTransaction);
}
return fruitTransactionList;
}

private FruitTransaction getFromCsvRow(String row) {
String[] fields = row.split(COMMA_SEPARATOR);
FruitTransaction fruitTransaction = new FruitTransaction();
fruitTransaction.setOperation(FruitTransaction.Operation.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;
}
}
27 changes: 27 additions & 0 deletions src/main/java/service/impl/FileReaderImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package service.impl;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import service.FileReader;

public class FileReaderImpl implements FileReader {

public FileReaderImpl() {

}
Comment on lines +11 to +13

Choose a reason for hiding this comment

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

Suggested change
public FileReaderImpl() {
}

Choose a reason for hiding this comment

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

fix comment


@Override
public List<String> read(String fileName) {
File file = new File(fileName);
if (!file.exists()) {
throw new IllegalArgumentException("File not found: " + fileName);
}
try {
return Files.readAllLines(file.toPath());
} catch (IOException e) {
throw new RuntimeException("Error reading file: " + fileName, e);
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/service/impl/FileWriterImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package service.impl;

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

public class FileWriterImpl {
public void write(String filePath, String content) {
if (!AutoCloseable.class.isAssignableFrom(FileWriter.class)) {
throw new IllegalStateException("FileWriter does not implement AutoCloseable");
}
try (FileWriter fileWriter = new FileWriter(filePath)) {

Choose a reason for hiding this comment

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

check if FileWriter implements AutoCloseable

fileWriter.write(content);
} catch (IOException e) {
throw new RuntimeException("Error writing to file: " + filePath, e);
}
}
}
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.

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

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

public class ReportGeneratorImpl implements ReportGenerator {
private static final String HEADER = "fruit,amount";
private static final String COMMA_DELIMITER = ",";
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(HEADER).append(System.lineSeparator());
for (Map.Entry<String, Integer> entry : fruits.entrySet()) {
report.append(entry.getKey())
.append(COMMA_DELIMITER)
.append(entry.getValue())
.append(System.lineSeparator());
}

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());
}
}
11 changes: 11 additions & 0 deletions src/main/java/service/operation/ReturnOperation.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 ReturnOperation implements OperationHandler {
@Override
public void performOperation(FruitTransaction fruitTransaction) {
Storage.add(fruitTransaction.getFruit(), fruitTransaction.getQuantity());
}
}
11 changes: 11 additions & 0 deletions src/main/java/service/operation/SupplyOperation.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 SupplyOperation implements OperationHandler {
@Override
public void performOperation(FruitTransaction fruitTransaction) {
Storage.add(fruitTransaction.getFruit(), fruitTransaction.getQuantity());
}
}
Loading