Volodymyr Kupriienko
What is clean code?
# Intro
Analyze examples
Define heuristic rules
Multithreading
Architecture
# Intro
# Intro
Software development practice described in book written by Robert C. Martin (uncle Bob)
Чистий код можуть читати і удосконалювати інші розробники, крім його вихідного автора.
Чистий код - це код, над яким ретельно попрацювали. Хтось не пошкодував часу, щоб зробити його простим і чітким. Хтось приділив належну увагу всім дрібницям і поставився до коду з душею.
func main() {
c := &User{Name: "Kate", Ch: make(chan string)}
arrEmployees := []*Employee{
{Name: "John", Type: 1},
{Name: "Liza", Type: 2},
{Name: "Garry", Type: 2},
}
go func() {
for _, e := range arrEmployees {
if e.Type == 2 {
c.Ch <- fmt.Sprintf("Hello, I'm %s", e.Name)
time.Sleep(time.Second)
c.Ch <- fmt.Sprintf("Waiting time is 5m")
break
}
}
close(c.Ch)
}()
for m := range c.Ch {
fmt.Println(m)
}
}
type User = {...}
type Employee = {...}
const c: User = {
name: "Kate",
ch: (m: string) => console.log(m),
};
const arrEmployees: Employee[] = [
{name: "John", type: 1},
{name: "Liza", type: 2},
{name: "Garry", type: 2},
]
for (let e of arrEmployees) {
if (e.type == 2) {
c.ch(`Hello, I'm ${e.name}`)
setTimeout(
() => c.ch("Waiting time is 5m"),
1000
)
break
}
}
# Intro
Naming
# Naming
-type User struct {
- Name string
- Ch chan string
}
+type Passenger struct {
+ FirstName string
+ NotificationChannel chan string
}
-type Employee struct {
- Name string
- Type int
}
+type DriverRank int
+
+const (
+ DriverRankStandard DriverRank = 1
+ DriverRankPremium DriverRank = 2
+)
+
+type Driver struct {
+ FirstName string
+ Rank DriverRank
}
-c := &User{Name: "Kate", Ch: make(chan string)}
+passenger := &Passenger{
+ FirstName: "Kate",
+ NotificationChannel: make(chan string)
+}
-arrEmployees := []*Employee{
- {Name: "John", Type: 1},
- {Name: "Liza", Type: 2},
- {Name: "Garry", Type: 2},
}
+availableDrivers := []*Driver{
+ {FirstName: "John", Rank: DriverRankStandard},
+ {FirstName: "Liza", Rank: DriverRankPremium},
+ {FirstName: "Garry", Rank: DriverRankPremium},
}
-type User = {
- name: string
- ch: (m: string) => void
}
+type channel = (update: string) => void
+
+type Passenger = {
+ firstName: string
+ notificationChannel: channel
}
-type Employee = {
- name: string
- type: number
}
+enum Rank {
+ STANDARD = 1,
+ PREMIUM = 2,
+}
+
+type Driver = {
+ firstName: string
+ rank: Rank
}
-const c: User = {
- name: "Kate",
- ch: (m: string) => console.log(m),
};
+const passengerKate: Passenger = {
+ firstName: "Kate",
+ notificationChannel: (update: string) => console.log(update),
};
-const arrEmployees: Employee[] = [
- {name: "John", type: 1},
- {name: "Liza", type: 2},
- {name: "Garry", type: 2},
]
+const availableDrivers: Driver[] = [
+ {firstName: "John", rank: Rank.STANDARD},
+ {firstName: "Liza", rank: Rank.PREMIUM},
+ {firstName: "Garry", rank: Rank.PREMIUM},
]
# Naming
go func() {
- for _, e := range arrEmployees {
- if e.Type == 2 {
- c.Ch <- fmt.Sprintf("Hello, I'm %s", e.Name)
time.Sleep(time.Second)
- c.Ch <- fmt.Sprintf("Waiting time is 5m")
break
}
}
- close(c.Ch)
+ for _, driver := range availableDrivers {
+ if driver.Rank == DriverRankPremium {
+ passenger.NotificationChannel
+ <- fmt.Sprintf("Hello, I'm %s", driver.Name)
time.Sleep(time.Second)
+ passenger.NotificationChannel
+ <- fmt.Sprintf("Waiting time is 5m")
break
}
}
+ close(passenger.NotificationChannel)
}()
-for m := range c.Ch {
- fmt.Println(m)
}
+for update := range passenger.NotificationChannel {
+ fmt.Println(update)
}
-for (let e of arrEmployees) {
- if (e.type == 2) {
- c.ch(`Hello, I'm ${e.name}`)
setTimeout(
- () => c.ch("Waiting time is 5m"),
1000
)
break
}
}
+for (let driver of availableDrivers) {
+ if (driver.rank == Rank.PREMIUM) {
+ passenger.notificationChannel(
+ `Hello, I'm ${driver.name}`
+ )
setTimeout(
+ () => passenger.notificationChannel(
+ "Waiting time is 5m"
+ ),
1000
)
break
}
}
# Naming
Naming ✅
Methods
# Methods
# Methods
+func callTaxi(passenger *Passenger) {
for _, driver := range availableDrivers {
if driver.Rank == DriverRankPremium {
passenger.NotificationChannel <- fmt.Sprintf(
"Hello, I'm %s",
driver.Name
)
time.Sleep(time.Second)
passenger.NotificationChannel
<- fmt.Sprintf("Waiting time is 5m")
break
}
}
close(passenger.NotificationChannel)
+}
+func notifyPassengerOnUpdates(passenger *Passenger) {
for update := range passenger.NotificationChannel {
fmt.Println(update)
}
+}
+const callTaxi = (passenger: Passenger) => {
for (let driver of this.availableDrivers) {
if (driver.rank == Rank.PREMIUM) {
passenger.notificationChannel(
`Hello, I'm ${driver.name}`
)
setTimeout(
() => passenger.notificationChannel(
"Waiting time is 5m"
),
1000
)
break
}
}
+}
# Methods
func callTaxi(customer *Customer) {
for _, driver := range availableDrivers {
if driver.Rank == DriverRankPremium {
passenger.NotificationChannel <- fmt.Sprintf(
"Hello, I'm %s",
driver.Name
)
time.Sleep(time.Second)
passenger.NotificationChannel
<- fmt.Sprintf("Waiting time is 5m")
break
}
}
close(passenger.NotificationChannel)
}
func notifyPassengerOnUpdates(passenger *Passenger) {
for update := range passenger.NotificationChannel {
fmt.Println(update)
}
}
const callTaxi = (passenger: Passenger) => {
for (let driver of this.availableDrivers) {
if (driver.rank == Rank.PREMIUM) {
passenger.notificationChannel(
`Hello, I'm ${driver.name}`
)
setTimeout(
() => passenger.notificationChannel(
"Waiting time is 5m"
),
1000
)
break
}
}
}
Ящо це метод, завжди застосовую до нього прийом “вилучення методу”; у результаті в мене залишається основний метод, який більш чітко пояснює, що саме він робить, і декілька підметодів, що пояснюють, як він це робить.
# Methods
func callTaxi(passenger *Passenger) {
- for _, driver := range availableDrivers {
- if driver.Rank == DriverRankPremium {
+ driver := findDriver(DriverRankPremium)
passenger.NotificationChannel <- fmt.Sprintf(
"Hello, I'm %s",
driver.Name
)
time.Sleep(time.Second)
passenger.NotificationChannel
<- fmt.Sprintf("Waiting time is 5m")
- break
- }
- }
close(passenger.NotificationChannel)
}
+func findDriver(rank DriverRank) *Driver {
+ availableDrivers := []*Driver{
+ {FirstName: "John", Rank: DriverRankStandard},
+ {FirstName: "Liza", Rank: DriverRankPremium},
+ {FirstName: "Garry", Rank: DriverRankPremium},
+ }
+
+ for _, driver := range availableDrivers {
+ if driver.Rank == rank {
+ return driver
+ }
+ }
+
+ return nil
+}
func notifyPassengerOnUpdates(passenger *Passenger) {
for update := range passenger.NotificationChannel {
fmt.Println(update)
}
}
const callTaxi = (passenger: Passenger) => {
- for (let driver of this.availableDrivers) {
- if (driver.rank == Rank.PREMIUM) {
+ const driver = findDriver(Rank.PREMIUM)
passenger.notificationChannel(
`Hello, I'm ${driver.name}`
)
setTimeout(
() => passenger.notificationChannel(
"Waiting time is 5m"
),
1000
)
- break
- }
- }
}
+const findDriver = (rank: Rank): Driver => {
+ const availableDrivers: Driver[] = [
+ {firstName: "John", rank: Rank.STANDARD},
+ {firstName: "Liza", rank: Rank.PREMIUM},
+ {firstName: "Garry", rank: Rank.PREMIUM},
+ ]
+
+ return availableDrivers.find(
+ (driver: Driver) => driver.rank === rank
+ )
+}
Tests
Methods ✅
Naming ✅
# Tests
Код без тестів не можна вважати чистим, хоч бияким елегантним він не був і хоч би я добре читався.
Write test scenarios 💭
# Tests
Write code 👨🏻💻
Run tests 🚀
Fix issues 🪲
Refactor code 🧹
Repeat 😉
Закон Леблана: потім означає ніколи
Naming ✅
Methods ✅
Tests ✅
Comments
Formatting ✅
Naming ✅
Methods ✅
Tests ✅
# Comments
# Comments
# Comments
# Comments
Comments ✅
Formatting ✅
Objects & error handling
Naming ✅
Methods ✅
Tests ✅
Naming matters
We are reading code mostly
We work in a team
Improves stability and reliability
Boosts development process
All loves good quality code
💙💛
👇🏻