Skip to content
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
12 changes: 11 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
plugins {
id 'java'
id 'war'
}

compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'

group = 'com.walking.servletpractice'
version = '1.0-SNAPSHOT'

Expand All @@ -10,10 +14,16 @@ repositories {
}

dependencies {
compileOnly 'jakarta.servlet:jakarta.servlet-api:6.1.0'

testImplementation platform('org.junit:junit-bom:5.9.1')
testImplementation 'org.junit.jupiter:junit-jupiter'
}

test {
useJUnitPlatform()
}
}

war {
archiveFileName = 'calculation.war'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.walking.servletpractice.converter;

import com.walking.servletpractice.model.Calculation;
import com.walking.servletpractice.model.CalculationData;
import com.walking.servletpractice.model.dto.CalculationDto;

import java.util.stream.Collectors;

/**
* Преобразует объект бизнес-логики {@code Calculation} в dto-объект {@code CalculationDto},
* для отправки пользователю.
*/
Copy link
Owner

Choose a reason for hiding this comment

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

избыточная документация - она как лишние гандоны. Надежно, но портит ощущения

public class CalculationConverter implements Converter<Calculation, CalculationDto> {
@Override
public CalculationDto convert(Calculation calculation) {
var calculationDto = new CalculationDto();

calculationDto.setType(calculation.getType()
.getValue());
Copy link
Owner

@KFalcon2022 KFalcon2022 Jul 3, 2025

Choose a reason for hiding this comment

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

спорный момент. Если клиентом выступает фронтовая апликуха - она уж как-нибудь сама строковый литерал для енама смапит в то, что ей нужно. Если же у тебя сервер раздает фактическое отображение (html или что ты собрался в браузере рисовать) - зачем тебе вообще DTO? Конвертируй сразу доменный объект в страничку:)

На самом деле, расклад домен->дто->html тоже имеет право на жизнь, но не в этой же задачке на 4 строки кода. Тут просто не на чем пордемонстрировать разумность такого подхода


calculationDto.setData(calculation.getData()
.stream()
Copy link
Owner

Choose a reason for hiding this comment

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

не пихай цепочки стримов в параметр. Всегда выглядит как колхоз и читается дерьмово

.map(CalculationData::toString)
Copy link
Owner

@KFalcon2022 KFalcon2022 Jul 3, 2025

Choose a reason for hiding this comment

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

Строго говоря, toString() не особо предназначен для таких ситуаций. Так можно делать в совсем учебных задачках для начинающих, но здесь уже моветон. toString() ок для каких-нить логов, мб в каких-то редких случаях - как часть exception message. Но точно не в основной логике. Хотя бы потому что в логике может потребоваться несколько различных вариантов отображений в виде строки, в зависимости от контекста использования

Нужен строковый эквивалент объекта - выдели отдельный метод или класс, который будет превращать твою модель в строку

.collect(Collectors.toList()));
Copy link
Owner

Choose a reason for hiding this comment

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

Тут же джава повыше 11? Тогда зачем изменяемая коллекция?


calculationDto.setResult(calculation.getResult()
.toString());
Copy link
Owner

Choose a reason for hiding this comment

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

Зачем ты вообще отображаешь объект как строку - тоже открытый вопрос. Того же рода, что и конвертация енама в хрен пойми что:)


return calculationDto;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.walking.servletpractice.converter;

import com.walking.servletpractice.model.Calculation;
import com.walking.servletpractice.model.request.CalculationRequest;
import com.walking.servletpractice.service.CalculationService;

/**
* Преобразует dto-объект {@code CalculationRequest} в объект бизнес-логики {@code Calculation}.
* Объект {@code Calculation} запрашивается у {@code CalculationService}, который отвечает за вычисления.
*/
public class CalculationRequestConverter implements Converter<CalculationRequest, Calculation> {
private final CalculationService calculationService;

public CalculationRequestConverter(CalculationService calculationService) {
this.calculationService = calculationService;
}

@Override
public Calculation convert(CalculationRequest source) {
return calculationService.calculated(source.getType(), source.getData());
Copy link
Owner

Choose a reason for hiding this comment

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

Конвертер должен заниматься конвертацией - подготовкой моделей одного архитектурного слоя для использования в другом. Но не вызывать бинзес-логику

Copy link
Owner

Choose a reason for hiding this comment

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

В данном случае я вообще не вижу смысла в конвертере, проще в сервис два аргумента передать прямо в сервлете (или откуда ты этот конвертер вызываешь)

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.walking.servletpractice.converter;

public interface Converter<S, R> {
R convert(S source);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.walking.servletpractice.exception;

public class ParsingException extends RuntimeException {
public ParsingException() {
Copy link
Owner

Choose a reason for hiding this comment

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

зачем?

}

public ParsingException(String value, Throwable cause) {
super("Cannot parse value: '%s'".formatted(value), cause);
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/walking/servletpractice/model/Calculation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.walking.servletpractice.model;
Copy link
Owner

Choose a reason for hiding this comment

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

тут можно потиху заводить мой любимый пакет - domain. Те же модельки, но для доменного слоя. А model оставить для dto и прочего вспомогательного говна


import java.util.List;

/**
* Объект бизнес-логики отражающий конкретное вычисление. Инкапсулирует данные
Copy link
Owner

Choose a reason for hiding this comment

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

Это что угодно, но не объект Объект бизнес-логики:)

Доменная моделька - да

* о типе вычисления, данных для вычисления и результате вычисления.
*/
public class Calculation {
private final CalculationType type;
private final List<CalculationData> data;
Copy link
Owner

Choose a reason for hiding this comment

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

дело хозяйское, но не инициализированный список - это предвестник неожиданного NPE:)

private final CalculationResult result;

public Calculation(CalculationType type, List<CalculationData> data, CalculationResult result) {
this.type = type;
this.data = data;
this.result = result;
}

public CalculationType getType() {
return type;
}

public List<CalculationData> getData() {
return data;
}

public CalculationResult getResult() {
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.walking.servletpractice.model;

/**
* Объект-значение инкапсулирующий данные, используемые для вычислений.
*/
public class CalculationData {
private final double value;

public CalculationData(double value) {
this.value = value;
}

public double getValue() {
return value;
}

@Override
public String toString() {
return Double.toString(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.walking.servletpractice.model;

/**
* Объект-значение инкапсулирующий значение результата вычисления.
*/
public class CalculationResult {
private final double value;

public CalculationResult(double value) {
this.value = value;
}

public double getValue() {
return value;
}

@Override
public String toString() {
return Double.toString(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.walking.servletpractice.model;

import java.util.NoSuchElementException;

/**
* Ограничивает список типов арифметических операций, которые могут быть обработаны.
*/
public enum CalculationType {
ADDITION("addition"),
SUBTRACTION("subtraction"),
MULTIPLICATION("multiplication"),
DIVISION("division");

private final String value;

CalculationType(String value) {
this.value = value;
}

public String getValue() {
return value;
}

public static CalculationType getByValue(String value) {
for (CalculationType type : CalculationType.values()) {
if (type.getValue()
.equals(value)) {
Copy link
Owner

Choose a reason for hiding this comment

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

такие переносы очень плохо читаются. Хочешь переносить - выдели в переменную. Но как по мне здесь и в одну строку норм - NPE негде упасть

return type;
}
}

throw new NoSuchElementException(
"Cannot get CalculationType from value: '%s'".formatted(value));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.walking.servletpractice.model.dto;

import java.util.List;
import java.util.StringJoiner;

/**
* Dto-объект, содержащий данные вычисления, возвращаемые пользователю.
*/
public class CalculationDto {
private static final String ARGUMENT_DELIMITER = "&";
private static final String NAME_VALUE_DELIMITER = "=";
private static final String VALUES_DELIMITER = ",";

private String type;
private List<String> data;
private String result;

public void setType(String type) {
this.type = type;
}

public void setData(List<String> data) {
this.data = data;
}

public void setResult(String result) {
this.result = result;
}

/**
* Преобразует данные вычислений в строку вида: "type=value&data=value1,valueN&result=value"
*/
@Override
public String toString() {
Copy link
Owner

Choose a reason for hiding this comment

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

все о том же. Не под эту задачу данный метод

var argumentJoiner = new StringJoiner(ARGUMENT_DELIMITER);

addType(argumentJoiner);
addData(argumentJoiner);
addResult(argumentJoiner);

return argumentJoiner.toString();
}

private void addType(StringJoiner stringJoiner) {
stringJoiner.add("type" + NAME_VALUE_DELIMITER + type);
}

private void addData(StringJoiner stringJoiner) {
var valuesJoiner = new StringJoiner(VALUES_DELIMITER);

for (String value : data) {
valuesJoiner.add(value);
}

stringJoiner.add("data" + NAME_VALUE_DELIMITER + valuesJoiner);
}

private void addResult(StringJoiner stringJoiner) {
stringJoiner.add("result" + NAME_VALUE_DELIMITER + result);
}
Copy link
Owner

Choose a reason for hiding this comment

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

Я бы какой-нить отдельный класс-сериализатор выделил. Императивная сериализация внутри модели почти всегда стреляет в ногу

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.walking.servletpractice.model.request;

import com.walking.servletpractice.model.CalculationData;
import com.walking.servletpractice.model.CalculationType;

import java.util.List;

/**
* Dto-объект, содержащий данные для вычислений, полученные от пользователя.
*/
public class CalculationRequest {
private CalculationType type;
private List<CalculationData> data;

public CalculationType getType() {
return type;
}

public void setType(CalculationType type) {
this.type = type;
}

public List<CalculationData> getData() {
return data;
}

public void setData(List<CalculationData> data) {
this.data = data;
}
}
Loading