Probablemente todos los programadores, tarde o temprano, empiecen a pensar en la calidad de su código. Y lo más probable es que no me equivoque si digo que una buena mitad de los desarrolladores siempre están descontentos con ellos. Rara vez me gustó mi código tampoco: las funciones, al parecer, podrían hacerse más cortas, sería bueno eliminar también el anidamiento innecesario. Sería genial escribir pruebas y documentación, pero casi nunca tuve tiempo para ello.
Naturalmente, pasé mucho tiempo leyendo libros y todo tipo de artículos, tratando de descubrir cómo mejorar mi código. Pero la imagen no cuadraba. O las recomendaciones en libros o artículos eran demasiado generales y, a veces, contradictorias, o estaba en mí, pero, a pesar de los esfuerzos, hubo pocos resultados.
La situación cambió radicalmente después de que hice el curso HowToCode [enlace eliminado por el moderador, ya que rompe las reglas] . El curso describe un enfoque sistemático y, como todo brillante, simple y hermoso para el desarrollo, que combina análisis, diseño, documentación, pruebas y desarrollo de código. Todo el curso se basa en el uso del paradigma funcional y el lenguaje Scheme (un dialecto Lisp), sin embargo, las recomendaciones son bastante aplicables para otros lenguajes, y para JavaScript y TypeScript, a los que intenté adaptarlos, generalmente son multa.
Realmente me gustaron los resultados:
Primero, finalmente mi código se volvió legible, aparecieron pruebas y documentación clara.
En segundo lugar, el enfoque TDD funcionó, al que tomé varios enfoques, pero no pude comenzar a seguirlo de ninguna manera.
-, : , , ,
: - , , -
, , , - , , . - .
, .
, , - .
, , .
3 :
, , , , .
. : , Jira . .
, 2 : . , - . , - , , .
, , . .
:
1.
2. , .
1.
, :
: , , - -
, . - , , , , - , .
, : " web- , , . ."
, , . , , , ( ).
, , - . :
, , , . :
, , , . , - :
, , - .
2. ,
: 3 , : , .
- , . , , :
: , , , , ..
, , ,
: , ..
, . :
,
,
-
, , :
- . , - , . :
, ..
: ? , , , , , - .
:
:
, , , , ,
, , ,
, , .
, :
, - , . , , .
:
: , , , ..
:
-
..
, , :
, .
:
-,
-, ,
-, , , . , , , .
, . , , , .
- .
, , , .
?
-, . , , . , , .
-, TypeScript, .
-, , , unit-.
:
- , .
- .
. TypeScript JSDoc, - . - React JS, , (props) (state), React. , , , .
: HowToCode , - , . , , .
, .
, , Redux, .
. . , AppState - :
export interface AppState {}
. , , , . :
|
|
|
|
|
|
|
title |
|
|
+ |
|
|
backendAddress |
|
|
+ |
|
|
isLoading |
|
|
+ |
true - false - |
|
group |
|
Group |
- |
|
|
loadData |
|
|
+ |
|
:
export interface AppState {
title: string;
backendAddress: string;
isLoading: boolean;
group?: Group;
loadData: Function;
}
TypeScript , , , . , , . JavaScript - JSDoc.
/**
*
* @prop title -
* @prop backendAddress -
* @prop isLoading - (true - , false - )
* @prop group - . group
* @method loadData -
*/
export interface AppState {
title: string;
backendAddress: string;
isLoading: boolean;
group?: Group;
loadData: Function;
}
AppState Group. , AppState , - , , . . , . .
, , TODO - IDE TODO
/**
*
* @prop title -
* @prop backendAddress -
* @prop isLoading - (true - , false - )
* @prop group - . group
* @method loadData -
*/
export interface AppState {
title: string;
backendAddress: string;
isLoading: boolean;
group?: Group;
loadData: Function;
}
/**
*
*/
//TODO
export interface Group {
}
, .
, , - . , unit-, , - . , group - , . - :
/**
*
* @prop title -
* @prop backendAddress -
* @prop isLoading - (true - , false - )
* @prop group - . group
* @method loadData -
*/
export interface AppState {
title: string;
backendAddress: string;
isLoading: boolean;
group?: Group;
loadData: Function;
}
// 1
const appState1: AppState = {
title: " 1",
backendAddress: "/view_doc.html",
isLoading: true,
group: undefined,
loadData: () => {}
}
// 2
const appState2: AppState = {
title: " 2",
backendAddress: "/view_doc_2.html",
isLoading: false,
group: group1, //
loadData: () => {}
}
/**
*
*/
//TODO
export interface Group {
}
//TODO
const group1 = {}
, , , - . , , , - , "".
TODO , , , , .
. , , , , - :
export default abstract class AbstractService {
/**
*
* @fires get_group_data - ,
* @returns
*/
abstract getGroupData(): Promise<Group>;
}
, - , , .
, .
, , .
, - :
. - , :
,
,
. .
.
, :
(TODO),
,
React - , .
, , - , .
1 -
- , :
,
, , .
TypeScript - :
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
return "6 18";
}
:
getWorkDuration - , ,
worktimeFrom: string, worktimeTo: string - .
: string -
return "6 18" - ,
, , - unit - , .
2 : - . :
:
const componentName = (props: PropsType) => { return <h1>componentName</h1> }
:
class componentName extends React.Component<PropsType, StateType>{
state = {
//
}
render() {
return <h1>componentName</h1>
}
}
:
PropsType -
StateType -
- , , .
, App. , , , -. :
interface AppProps {}
export default class App extends Component<AppProps, AppState> {
state = {
title: " ",
backendAddress: "",
isLoading: true,
loadData: this.loadData.bind(this)
}
/**
*
*/
//TODO
loadData() {
}
render() {
return <h1>App</h1>
}
}
:
App "", AppProps
AppState , ,
loadData, , TODO,
2 -
, , , . JSDoc, , , .
, , , , , , . , :
/**
*
* , , -
* @param worktimeFrom - : ( 00:00 23:59)
* @param worktimeTo - : ( 00:00 23:59)
* @return Y?, 6 18 5,
*/
//TODO
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
return "6 18";
}
TODO, , .
- , , , , . , , , , - , - .
3.
, , . - unit-. , :
unit- , , - ,
unit- , . ,
, .
. , .
" " - , .
, , . "" enum. :
type TrafficLights = "" | "" | "";
, , TrafficLights : , , - :
function trafficLightsFunction (trafficLights: TrafficLights) {
switch (trafficLights) {
case "":
...
case "":
...
case "":
...
}
}
. , TrafficLights - , , "..." , .
, . , , , , 3 . .
, - , .
, - . , - , - . " " . , , , , . .
:
№ |
|
|
|
|
1 |
|
, . |
|
1 - 2 (true / false) - 1 , 0 |
2 |
|
, . |
.. |
, , , . , 100- , , 4 : 1 - 25, 26 - 50, 51 - 75, 76 - 100 4 . |
3 |
|
|
(0 - 300]
.. |
:
|
4 |
|
, , |
? , - , , . , . . " " - . - , , . |
, , -, . 2 :
- , . |
5 |
() |
, |
"" : id
.. |
2 . |
6 |
|
, |
, |
, : , , , , 2 |
, .
/**
*
* , , -
* @param worktimeFrom - : ( 00:00 23:59)
* @param worktimeTo - : ( 00:00 23:59)
* @return Y?, 6 18 5,
*/
//TODO
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
return "6 18";
}
, . string , - , :, , 00:00 23:59. , . , . 3 -:
,
, -
-
, . -, , 3 , , , 5
№ |
|
worktimeFrom |
worktimeTo |
|
1 |
worktimeFrom |
, , "24:00" |
, "18:00" |
|
2 |
worktimeFrom |
, "18:00" |
, , "24:00" |
|
3 |
, worktimeFrom < worktimeTo |
, worktimeTo, "00:00" |
, worktimeFrom, , "23:59" |
23 59 |
4 |
, worktimeFrom > worktimeTo |
, worktimeTo, "18:49" |
, worktimeFrom, , "10:49" |
16 |
5 |
worktimeFrom = worktimeTo |
, , "01:32" |
, , "01:32" |
0 |
- , : . . Jest Enzyme - React JS. , :
describe(' ', () => {
it(' , 0', ()=>{
const result = getWorkDuration("01:32", "01:32");
expect(result).toBe("0");
});
//
...
});
- : , , . -, , , - enzyme.
App. :
interface AppProps {}
export default class App extends Component<AppProps, AppState> {
state = {
title: " ",
backendAddress: "",
isLoading: true,
loadData: this.loadData.bind(this)
}
/**
*
*/
//TODO
loadData() {
}
render() {
return <h1>App</h1>
}
}
, , :
/**
*
* @prop title -
* @prop backendAddress -
* @prop isLoading - (true - , false - )
* @prop group - . group
* @method loadData -
*/
export interface AppState {
title: string;
backendAddress: string;
isLoading: boolean;
group?: Group;
loadData: Function;
}
, :
-, , .
-, . , group , , - . , "".
, , :
() 2 ,
, . , : .
, 4 :
2 - , , "" , ,
2 - , ,
, , 2 .
loadData - , , - , - . , , , , , .
- loadData :
import React from 'react';
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });
import App from './App';
describe('App', () => {
test(' App , loadData', () => {
// loadData
const loadData = jest.spyOn(App.prototype, 'loadData');
//
const wrapper = mount(<App></App>);
// loadData
expect(loadData.mock.calls.length).toBe(1);
});
}
enzyme , . :
loadData ,
( )
,
- .
test(' , ', () => {
//
const wrapper = mount(<App></App>);
//
wrapper.setState({
title: " 1",
backendAddress: "/view_doc.html",
isLoading: true,
group: undefined,
loadData: () => {}
})
//
//
expect(wrapper.find('h1').length).toBe(1);
expect(wrapper.find('h1').text()).toBe(" 1");
//,
expect(wrapper.find(Spinner).length).toBe(1);
//,
expect(wrapper.find(Group).length).toBe(0);
});
:
. , . ,
, , 2 , :
-
. , , .
, , .
:
test(' , . ', () => {
const wrapper = mount(<App></App>);
wrapper.setState({
title: " 2",
backendAddress: "/view_doc_2.html",
isLoading: false,
group: {
id: "1",
name: " 1",
listOfCollaborators: []
},
loadData: () => {}
})
expect(wrapper.find('h1').length).toBe(1);
expect(wrapper.find('h1').text()).toBe(" 2");
expect(wrapper.find(Spinner).length).toBe(0);
expect(wrapper.find(Group).length).toBe(1);
});
, , , , . , , , .
, , . , .
4 5.
, . , , , . , :
-, ,
-, , Knowledge Shift ( ).
. , , , . , , - .
, . :
/**
*
* , , -
* @param worktimeFrom - : ( 00:00 23:59)
* @param worktimeTo - : ( 00:00 23:59)
* @return Y?, 6 18 5,
*/
//TODO
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
return "6 18";
}
, , , ":", , :
, , , 00:00
, ,
X Y?,
:
":", ..
.
:
/**
*
* , , -
* @param worktimeFrom - : ( 00:00 23:59)
* @param worktimeTo - : ( 00:00 23:59)
* @return Y?, 6 18 5,
*/
export const getWorkDuration = (worktimeFrom: string, worktimeTo: string): string => {
const worktimeFromInMinutes = getWorktimeToMinutes(worktimeFrom);
const worktimeToInMinutes = getWorktimeToMinutes(worktimeTo);
const minutesDiff = calcDiffBetweenWorktime(worktimeFromInMinutes, worktimeToInMinutes);
return convertDiffToString(minutesDiff);
}
/**
* c ,
* @param worktimeFrom - : ( 00:00 23:59)
* @returns , 00 00
*/
//TODO
export const getWorktimeToMinutes = (worktime: string): number => {
return 0;
}
/**
*
* @param worktimeFrom - ,
* @param worktimeTo - ,
* @returns
*/
//TODO
export const calcDiffBetweenWorktime = (worktimeFrom: number, worktimeTo: number): number => {
return 0;
}
/**
* Y?
* @param minutes -
* @returns Y?, 6 18 5
*/
//TODO
export const convertDiffToString = (minutes: number): string => {
return "6 18";
}
, , , . . , TODO, . . , , .
, . , , , , - , .
Knowledge Shift
, , Knowledge Shift.
, , , , , , - () . , , , .
, , - , , . Knowledge Domain Shift Knowledge Shift.
, , , , .
, , - App:
export default class App extends Component<AppProps, AppState> {
state = {
title: " ",
backendAddress: "",
isLoading: true,
group: undefined,
loadData: this.loadData.bind(this)
}
/**
*
*/
//TODO
loadData() {}
componentDidMount() {
// loadData
this.loadData();
}
render() {
const {isLoading, group, title} = this.state;
return (
<div className="container">
<h1>{title}</h1>
{
isLoading ?
<Spinner/>
// Group
: <Group group={group}></Group>
}
</div>
);
}
}
componentDidMount, . render. , , - , Group.
, , c . group , , App - , . , , , , , , .
, , . , , TODO : + . , TODO .
Cuando llegue ese maravilloso momento, simplemente inicie la aplicación y disfrute de cómo funciona. No se bloquea debido a errores perdidos o escenarios olvidados y no realizados, sino que simplemente funciona.
Este es, en general, el enfoque completo. No es difícil, pero requiere hábito y disciplina. Al igual que con cualquier negocio complicado, el mayor desafío es comenzar y resistir la tentación de dejar de fumar en las primeras parejas. Si esto tiene éxito, después de un tiempo ni siquiera querrá pensar en cómo escribir código a la antigua usanza: sin pruebas, documentación y con funciones largas e incomprensibles. ¡Buena suerte!