Merge branches 'feature/student' and 'feature/custom-list' into feature/ui
This commit is contained in:
@@ -0,0 +1,122 @@
|
|||||||
|
# Работа с зависимостями между Git-ветками
|
||||||
|
|
||||||
|
## Сценарий
|
||||||
|
|
||||||
|
Предположим, мы находимся в ветке:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
feature/student
|
||||||
|
```
|
||||||
|
|
||||||
|
В этой ветке реализуется функциональность, связанная со `Student`.
|
||||||
|
|
||||||
|
При этом у нас появляется новая, независимая по смыслу фича:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
feature/sorting
|
||||||
|
```
|
||||||
|
|
||||||
|
Для реализации сортировки нам необходим класс или интерфейс `Student`, который уже был создан в `feature/student`.
|
||||||
|
|
||||||
|
## Как создать новую ветку
|
||||||
|
|
||||||
|
Если нам необходимо продолжить разработку `feature/sorting`, используя текущее состояние `feature/student`, создаём новую ветку **от `feature/student`**.
|
||||||
|
|
||||||
|
Сначала переключаемся на `feature/student`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout feature/student
|
||||||
|
```
|
||||||
|
|
||||||
|
Затем создаём новую ветку:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git branch feature/sorting
|
||||||
|
```
|
||||||
|
|
||||||
|
После этого история будет выглядеть примерно так:
|
||||||
|
|
||||||
|
```text
|
||||||
|
feature/sorting
|
||||||
|
/
|
||||||
|
---------------●
|
||||||
|
\
|
||||||
|
feature/student
|
||||||
|
```
|
||||||
|
|
||||||
|
Обе ветки на момент создания указывают на один и тот же коммит.
|
||||||
|
|
||||||
|
Теперь в `feature/sorting` доступен весь код, который был добавлен в `feature/student`, в том числе `Student`.
|
||||||
|
|
||||||
|
Чтобы перейти в новую ветку:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout feature/sorting
|
||||||
|
```
|
||||||
|
|
||||||
|
## Почему важно создавать ветку именно таким образом
|
||||||
|
|
||||||
|
В данном случае `feature/sorting` зависит от кода, находящегося в `feature/student`.
|
||||||
|
|
||||||
|
При этом сами фичи логически разные:
|
||||||
|
|
||||||
|
- `feature/student` — разработка `Student`;
|
||||||
|
- `feature/sorting` — реализация сортировки;
|
||||||
|
- `Student` нужен в `feature/sorting` как зависимость для реализации сортировки.
|
||||||
|
|
||||||
|
Поэтому не стоит продолжать разработку сортировки непосредственно в `feature/student`. Лучше создать отдельную ветку от того состояния, где необходимый код уже существует.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Если ветки уже разошлись
|
||||||
|
|
||||||
|
Иногда невозможно создать `feature/sorting` непосредственно от нужного места в истории `feature/student`.
|
||||||
|
|
||||||
|
Например, `feature/sorting` уже была создана раньше:
|
||||||
|
|
||||||
|
```text
|
||||||
|
A---B---C---D feature/student
|
||||||
|
\
|
||||||
|
E---F---G feature/sorting
|
||||||
|
```
|
||||||
|
|
||||||
|
В таком случае можно получить изменения из `feature/student` посредством слияния (merge).
|
||||||
|
|
||||||
|
Находясь в `feature/sorting`, выполняем:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout feature/sorting
|
||||||
|
git merge feature/student
|
||||||
|
```
|
||||||
|
|
||||||
|
История станет примерно такой:
|
||||||
|
|
||||||
|
```text
|
||||||
|
A---B---C---D feature/student
|
||||||
|
\ \
|
||||||
|
E---F---G---M feature/sorting
|
||||||
|
```
|
||||||
|
|
||||||
|
Коммит `M` — результат слияния двух веток.
|
||||||
|
|
||||||
|
После этого в `feature/sorting` будут доступны изменения из `feature/student`, в том числе необходимый `Student`.
|
||||||
|
|
||||||
|
## Главное правило
|
||||||
|
|
||||||
|
**Если новая фича зависит от кода из другой ветки, но при этом является самостоятельной по смыслу — создаём для неё отдельную ветку, используя нужную ветку как основу.**
|
||||||
|
|
||||||
|
Если новую ветку можно создать непосредственно от нужного состояния исходной ветки:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout feature/student
|
||||||
|
git branch feature/sorting
|
||||||
|
```
|
||||||
|
|
||||||
|
Если ветки уже разошлись и создать её от нужного состояния невозможно:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout feature/sorting
|
||||||
|
git merge feature/student
|
||||||
|
```
|
||||||
|
|
||||||
|
Таким образом, мы сохраняем логическое разделение фич и одновременно можем использовать необходимый код из другой ветки.
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
import java.util.Arrays;
|
||||||
|
public class MyLinkedList<T> implements MyList<T>
|
||||||
|
{
|
||||||
|
private Node<T> head;
|
||||||
|
private int size;
|
||||||
|
public void add(T value) {
|
||||||
|
if (head == null) {
|
||||||
|
this.head = new Node<>(value);
|
||||||
|
} else {
|
||||||
|
Node<T> temp = head;
|
||||||
|
while (temp.getNext() != null) {
|
||||||
|
temp = temp.getNext();
|
||||||
|
}
|
||||||
|
temp.setNext(new Node<>(value));
|
||||||
|
}
|
||||||
|
size++;
|
||||||
|
}
|
||||||
|
public T get(int index)
|
||||||
|
{
|
||||||
|
int currentIndex = 0;
|
||||||
|
Node<T> temp = head;
|
||||||
|
while(temp != null)
|
||||||
|
{
|
||||||
|
if (currentIndex == index)
|
||||||
|
{
|
||||||
|
return temp.getValue();
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
temp = temp.getNext();
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
public void remove(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= size) {
|
||||||
|
throw new IndexOutOfBoundsException();
|
||||||
|
}
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
head = head.getNext();
|
||||||
|
size--;
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
int currentIndex = 0;
|
||||||
|
Node<T> temp = head;
|
||||||
|
while (temp != null)
|
||||||
|
{
|
||||||
|
if (currentIndex == index-1)
|
||||||
|
{
|
||||||
|
temp.setNext(temp.getNext().getNext());
|
||||||
|
size--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
temp = temp.getNext();
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public T set(int index, T value) {
|
||||||
|
if (index < 0 || index >= size) {
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Node<T> temp = head;
|
||||||
|
for (int i = 0; i < index; i++) {
|
||||||
|
temp = temp.getNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
T oldValue = temp.getValue();
|
||||||
|
temp.setValue(value);
|
||||||
|
return oldValue;
|
||||||
|
}
|
||||||
|
public int size() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return size == 0;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
Object[] res = new Object[size];
|
||||||
|
int index = 0;
|
||||||
|
Node<T> temp = head;
|
||||||
|
while (temp != null) {
|
||||||
|
res[index] = temp.getValue();
|
||||||
|
index++;
|
||||||
|
temp = temp.getNext();
|
||||||
|
}
|
||||||
|
return Arrays.toString(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Node<T> {
|
||||||
|
private T value;
|
||||||
|
private Node<T> next;
|
||||||
|
|
||||||
|
public Node(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNext(Node<T> next) {
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node<T> getNext() {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package ru.project.collection;
|
||||||
|
|
||||||
|
public interface MyList<T> {
|
||||||
|
|
||||||
|
void add(T element);
|
||||||
|
|
||||||
|
T get(int index);
|
||||||
|
|
||||||
|
T set(int index, T element);
|
||||||
|
|
||||||
|
void remove(int index);
|
||||||
|
|
||||||
|
int size();
|
||||||
|
|
||||||
|
boolean isEmpty();
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package ru.project.student;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class Student {
|
||||||
|
private final String groupNumber;
|
||||||
|
private final double averageGrade;
|
||||||
|
private final String recordBookNumber;
|
||||||
|
|
||||||
|
Student(String groupNumber, double averageGrade, String recordBookNumber) {
|
||||||
|
if (groupNumber == null || groupNumber.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Group number cannot be empty");
|
||||||
|
}
|
||||||
|
if (averageGrade < 0.0 || averageGrade > 5.0) {
|
||||||
|
throw new IllegalArgumentException("Average grade must be between 0.0 and 5.0");
|
||||||
|
}
|
||||||
|
if (recordBookNumber == null || recordBookNumber.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Record book number cannot be empty");
|
||||||
|
}
|
||||||
|
this.groupNumber = groupNumber;
|
||||||
|
this.averageGrade = averageGrade;
|
||||||
|
this.recordBookNumber = recordBookNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupNumber() {
|
||||||
|
return groupNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAverageGrade() {
|
||||||
|
return averageGrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRecordBookNumber() {
|
||||||
|
return recordBookNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Student{" +
|
||||||
|
"groupNumber='" + groupNumber + '\'' +
|
||||||
|
", averageGrade=" + averageGrade +
|
||||||
|
", recordBookNumber='" + recordBookNumber + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hashCode() != obj.hashCode()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Student student = (Student) obj;
|
||||||
|
|
||||||
|
return Double.compare(averageGrade, student.averageGrade) == 0
|
||||||
|
&& Objects.equals(groupNumber, student.groupNumber)
|
||||||
|
&& Objects.equals(recordBookNumber, student.recordBookNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(groupNumber, averageGrade, recordBookNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package ru.project.student;
|
||||||
|
|
||||||
|
public class StudentBuilder {
|
||||||
|
private String groupNumber;
|
||||||
|
private double averageGrade;
|
||||||
|
private String recordBookNumber;
|
||||||
|
|
||||||
|
public StudentBuilder setGroupNumber(String groupNumber) {
|
||||||
|
this.groupNumber = groupNumber;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StudentBuilder setAverageGrade(double averageGrade) {
|
||||||
|
this.averageGrade = averageGrade;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StudentBuilder setRecordBookNumber(String recordBookNumber) {
|
||||||
|
this.recordBookNumber = recordBookNumber;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Student build() {
|
||||||
|
return new Student(groupNumber, averageGrade, recordBookNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
package ru.project.student;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
|
||||||
|
|
||||||
|
class StudentTest {
|
||||||
|
@Test
|
||||||
|
void shouldCreateValidStudent() {
|
||||||
|
Student student = new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertEquals("101", student.getGroupNumber());
|
||||||
|
assertEquals(4.5, student.getAverageGrade());
|
||||||
|
assertEquals("12345", student.getRecordBookNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenGroupNumberIsEmpty() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber("")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenAverageGradeIsGreaterThanFive() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(6.0)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenAverageGradeIsLessThanZero() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(-1.0)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCreateStudentWhenAverageGradeIsZero() {
|
||||||
|
Student student = new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(0.0)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertEquals(0.0, student.getAverageGrade());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldCreateStudentWhenAverageGradeIsFive() {
|
||||||
|
Student student = new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(5.0)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertEquals(5.0, student.getAverageGrade());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenRecordBookNumberIsEmpty() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("")
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenGroupNumberIsNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber(null)
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenGroupNumberIsBlank() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber(" ")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenRecordBookNumberIsNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber(null)
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowExceptionWhenRecordBookNumberIsBlank() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber(" ")
|
||||||
|
.build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnTrueWhenStudentsAreEqual() {
|
||||||
|
Student student1 = new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Student student2 = new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertEquals(student1, student2);
|
||||||
|
assertEquals(student1.hashCode(), student2.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReturnFalseWhenStudentsAreDifferent() {
|
||||||
|
Student student1 = new StudentBuilder()
|
||||||
|
.setGroupNumber("101")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Student student2 = new StudentBuilder()
|
||||||
|
.setGroupNumber("102")
|
||||||
|
.setAverageGrade(4.5)
|
||||||
|
.setRecordBookNumber("12345")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assertNotEquals(student1, student2);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user