🚚 Moved code from Gists
This commit is contained in:
commit
f7ad2f122f
|
@ -0,0 +1,49 @@
|
|||
# Задача для SWE - Basic
|
||||
Защита тестового задания состоит из 2 этапов:
|
||||
|
||||
1. Ревью решения
|
||||
2. Устное обсуждение кода
|
||||
|
||||
В случае успешного прохождения 1 этапа, вы будете приглашены на интервью в рамках которого нужно ответить на несколько вопросов по написанному коду и защитить свое решение
|
||||
|
||||
Нужно написать класс на Java/Swift/Golang/С (Си), в зависимости от вакансии на которую вы претендуете (другие задачи на других языках рассматриваться не будут), со следующим интерфейсом (псевдокод):
|
||||
|
||||
```java
|
||||
class Incrementor {
|
||||
/**
|
||||
* Возвращает текущее число. В самом начале это ноль.
|
||||
*/
|
||||
int getNumber();
|
||||
|
||||
/**
|
||||
* Увеличивает текущее число на один. После каждого вызова этого
|
||||
* метода getNumber() будет возвращать число на один больше.
|
||||
*/
|
||||
void incrementNumber();
|
||||
|
||||
/**
|
||||
* Устанавливает максимальное значение текущего числа.
|
||||
* Когда при вызове incrementNumber() текущее число достигает
|
||||
* этого значения, оно обнуляется, т.е. getNumber() начинает
|
||||
* снова возвращать ноль, и снова один после следующего
|
||||
* вызова incrementNumber() и так далее.
|
||||
* По умолчанию максимум -- максимальное значение int.
|
||||
* Если при смене максимального значения число начинает
|
||||
* превышать максимальное значение, то число надо обнулить.
|
||||
* Нельзя позволять установить тут число меньше нуля.
|
||||
*/
|
||||
void setMaximumValue(int maximumValue);
|
||||
}
|
||||
```
|
||||
|
||||
Т.е. класс очень простой. А теперь сложность: оно должно быть сделано очень хорошо. Т.е. максимально качественно, как только можно. Код должен быть идеальным, все должно быть покрыто unit тестами. Классы и все методы должны быть полностью покрыты понятной (т.е. полезной, а не для отписки) javadoc (для Java) или аналогом для Swift и Golang документацией. В общем, нужно сделать такой код, который каждый разработчик мечтает получить на поддержку -- идеальный (насколько кандидат способен).
|
||||
|
||||
Обратить внимание на:
|
||||
|
||||
- Форматирование кода.
|
||||
- Нейминг (названия всех сущностей).
|
||||
- Покрытие тестами.
|
||||
- Наличие документации.
|
||||
- Общая читабельность и простота кода.
|
||||
|
||||
Результат необходимо предоставить в виде скрытого gist-а на gist.github.com.
|
|
@ -0,0 +1,4 @@
|
|||
// Package incrementor предназначен для работы с инкрементными числовыми значениями.
|
||||
//
|
||||
// Поведение инкрементных чисел может быть индивидуально настроено, а их использование в горутинах безопасно.
|
||||
package incrementor
|
|
@ -0,0 +1,44 @@
|
|||
package incrementor_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"incrementor"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Example_direct() {
|
||||
// По-умолчанию значение инкрементного числа равно 0, а максимальный порог - максимально допустимому
|
||||
// значению int64.
|
||||
i := incrementor.New()
|
||||
|
||||
// Число увеличивается на 1 с каждым вызовом метода IncrementNumber.
|
||||
for j := 42; j < 42; j++ {
|
||||
i.IncrementNumber()
|
||||
}
|
||||
|
||||
// GetNumber показывает актуальное значение инкрементного числа.
|
||||
fmt.Println(i.GetNumber())
|
||||
|
||||
// SetMaximumValue устанавливает максимально допустимое значение инкрементного числа, при достижении которого
|
||||
// оно должно быть сброшено для отсчёта заново.
|
||||
i.SetMaximumValue(42)
|
||||
i.IncrementNumber()
|
||||
fmt.Println(i.GetNumber())
|
||||
|
||||
// Result: 41
|
||||
// Result: 0
|
||||
}
|
||||
|
||||
func Example_safe() {
|
||||
i := incrementor.New()
|
||||
|
||||
// Инкрементное число также может безопасно обновляться в горутинах.
|
||||
for j := 0; j <= 420; j++ {
|
||||
go i.IncrementNumber()
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println(i.GetNumber())
|
||||
|
||||
// Result: 420
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
module incrementor
|
||||
|
||||
go 1.14
|
||||
|
||||
require github.com/stretchr/testify v1.6.1
|
|
@ -0,0 +1,11 @@
|
|||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,99 @@
|
|||
package incrementor
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type (
|
||||
// Incrementor является инкрементным числом с настройками поведения, вроде максимально допустимого значения.
|
||||
Incrementor struct {
|
||||
// NOTE(toby3d): защита чтения/записи числа в горутинах
|
||||
mutex *sync.RWMutex
|
||||
|
||||
// NOTE(toby3d): непосредственно само инкрементное число
|
||||
number int64
|
||||
|
||||
// NOTE(toby3d): максимально допустимое значение инкрементного числа, по достижении которого оно будет
|
||||
// сброшено в 0
|
||||
maxValue int64
|
||||
}
|
||||
|
||||
// Reader описывает действия для чтения текущего значения числа.
|
||||
Reader interface {
|
||||
GetNumber() int64
|
||||
}
|
||||
|
||||
// Writer описывает действия для изменения значения числа и управления его максимально допустимым значением.
|
||||
Writer interface {
|
||||
IncrementNumber()
|
||||
SetMaximumValue(maximumValue int)
|
||||
}
|
||||
|
||||
// Incrementer описывает поведение менеджера инкрементного числа.
|
||||
Incrementer interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
)
|
||||
|
||||
// DefaultMaximumValue содержит максимально возможное значение числа по-умолчанию в битах.
|
||||
const DefaultMaximumValue = math.MaxInt64
|
||||
|
||||
// New создаёт новую структуру для хранения и управления инкрементным числом.
|
||||
//
|
||||
// NOTE(toby3d): в тестовом задании нет сведений по тому возможно ли изменение параметров на этапе инициализации, как
|
||||
// например отсчёт не с 0 или перезапись максимального порога без отдельного вызова SetMaximumValue. 🤷♂
|
||||
func New() *Incrementor {
|
||||
i := new(Incrementor)
|
||||
i.number = 0
|
||||
i.mutex = new(sync.RWMutex)
|
||||
i.maxValue = DefaultMaximumValue
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// GetNumber возвращает текущее число.
|
||||
func (i *Incrementor) GetNumber() int64 {
|
||||
i.mutex.RLock()
|
||||
defer i.mutex.RUnlock()
|
||||
|
||||
return i.number
|
||||
}
|
||||
|
||||
// IncrementNumber увеличивает текущее число на 1.
|
||||
//
|
||||
// Если в процессе выполнения метода число будет >= максимально допустимого порога, то оно будет сброшено в 0.
|
||||
func (i *Incrementor) IncrementNumber() {
|
||||
i.mutex.Lock()
|
||||
|
||||
i.number++
|
||||
|
||||
if i.number >= i.maxValue {
|
||||
i.number = 0
|
||||
}
|
||||
|
||||
i.mutex.Unlock()
|
||||
}
|
||||
|
||||
// SetMaximumValue устанавливает максимально возможное значение для числа. По достижении или превышении указанного
|
||||
// порога число будет сброшено в 0.
|
||||
//
|
||||
// Вводимое значение не может быть меньше 0 и больше DefaultMaximumValue.
|
||||
func (i *Incrementor) SetMaximumValue(maximumValue int64) {
|
||||
// NOTE(toby3d): предварительно проверяем ввод, косячный сбрасываем в значение по-умолчанию.
|
||||
if maximumValue <= 0 || maximumValue > DefaultMaximumValue {
|
||||
maximumValue = DefaultMaximumValue
|
||||
}
|
||||
|
||||
i.mutex.Lock()
|
||||
|
||||
i.maxValue = maximumValue
|
||||
|
||||
// NOTE(toby3d): если максимум оказывается меньше текущего значения, то сбрасываем число в 0.
|
||||
if i.number >= i.maxValue {
|
||||
i.number = 0
|
||||
}
|
||||
|
||||
i.mutex.Unlock()
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package incrementor //nolint: testpackage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const maxLoops int64 = 42 // NOTE(toby3d): максимальное число итераций в тестах
|
||||
|
||||
func TestGetNumber(t *testing.T) {
|
||||
i := New()
|
||||
assert.Zero(t, i.GetNumber())
|
||||
|
||||
for j := int64(1); j < maxLoops; j++ {
|
||||
i.IncrementNumber()
|
||||
|
||||
if !assert.Equal(t, j, i.GetNumber(), "number must be equal to loop step") {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncrementNumber(t *testing.T) {
|
||||
i := New()
|
||||
|
||||
for j := int64(1); j < maxLoops; j++ {
|
||||
i.IncrementNumber()
|
||||
|
||||
if !assert.Equal(t, j, i.number, "number must be equal to loop step index") {
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
i.SetMaximumValue(maxLoops)
|
||||
i.IncrementNumber()
|
||||
assert.Zero(t, i.number, "number should be reset when the maximum is reached")
|
||||
}
|
||||
|
||||
func TestSetMaximumValue(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string // NOTE(toby3d): имя кейса
|
||||
inputMax int64 // NOTE(toby3d): устанавливаемый порог
|
||||
expMax int64 // NOTE(toby3d): ожидаемое порог
|
||||
inputNumber int64 // NOTE(toby3d): изначальное значение числа
|
||||
expNumber int64 // NOTE(toby3d): ожидаемое значение числа
|
||||
}{
|
||||
{
|
||||
name: "zero max to default",
|
||||
inputMax: 0,
|
||||
expMax: DefaultMaximumValue,
|
||||
}, {
|
||||
name: "negative max to default",
|
||||
inputMax: -24,
|
||||
expMax: DefaultMaximumValue,
|
||||
}, {
|
||||
name: "valid max input",
|
||||
inputMax: 42,
|
||||
expMax: 42,
|
||||
}, {
|
||||
name: "number below new max",
|
||||
inputMax: 42,
|
||||
inputNumber: 24,
|
||||
expMax: 42,
|
||||
expNumber: 24,
|
||||
}, {
|
||||
name: "number above new max",
|
||||
expNumber: 0,
|
||||
inputNumber: 420,
|
||||
expMax: 42,
|
||||
inputMax: 42,
|
||||
},
|
||||
// NOTE(toby3d): число больше чем DefaultMaximumValue невозможно установить, так как оно приведёт к
|
||||
// ошибке компиляции: "constant 9223372036854775808 overflows int"
|
||||
} {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
i := New()
|
||||
i.number = tc.inputNumber
|
||||
|
||||
i.SetMaximumValue(tc.inputMax)
|
||||
|
||||
if !assert.Equal(t, i.maxValue, tc.expMax) || !assert.Equal(t, i.number, tc.expNumber) {
|
||||
t.FailNow()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue