Il est probable que chaque programmeur commence tôt ou tard à réfléchir à la qualité de son code. Et, très probablement, je ne me tromperai pas si je dis qu'une bonne moitié des développeurs sont toujours mécontents d'eux. J'ai rarement aimé mon code non plus: les fonctions, semble-t-il, pourraient être raccourcies, ce serait bien de supprimer également les imbrications inutiles. Ce serait formidable d'écrire des tests et de la documentation, mais j'en ai presque jamais eu le temps.
Naturellement, j'ai passé beaucoup de temps à lire des livres et toutes sortes d'articles, essayant de comprendre comment améliorer mon code. Mais l'image ne concordait pas. Soit les recommandations dans les livres ou les articles étaient trop générales et parfois contradictoires, soit c'était en moi, mais malgré les efforts, il y avait peu de résultats.
La situation a radicalement changé après avoir suivi le cours HowToCode [lien supprimé par le modérateur, depuis enfreint les règles] . Le cours décrit une approche systématique et, comme tout brillant, une approche simple et belle du développement, qui rassemble l'analyse, la conception, la documentation, les tests et le développement de code. L'ensemble du cours est construit sur l'utilisation du paradigme fonctionnel et du langage Scheme (un dialecte Lisp), cependant, les recommandations sont tout à fait applicables pour d'autres langages, et pour JavaScript et TypeScript, auxquels j'ai essayé de les adapter, elles sont généralement bien.
J'ai vraiment aimé les résultats:
Tout d'abord, finalement mon code est devenu lisible, une documentation claire et des tests sont apparus.
Deuxièmement, l'approche TDD a fonctionné, à laquelle j'ai adopté plusieurs approches, mais je n'ai pas pu commencer à la suivre en aucune façon
-, : , , ,
: - , , -
, , , - , , . - .
, .
, , - .
, , .
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 .
Lorsque ce moment merveilleux arrive, il vous suffit de lancer l'application et de profiter de son fonctionnement. Il ne plante pas à cause d'erreurs manquées ou de scénario oublié et non réalisé, mais fonctionne simplement.
C'est, en général, toute l'approche. Ce n'est pas difficile, mais cela demande de l'habitude et de la discipline. Comme pour toute entreprise délicate, le plus grand défi est de se lancer et de résister à l'envie d'arrêter dans les premiers couples. Si cela réussit, après un certain temps, vous ne voudrez même plus réfléchir à la façon d'écrire du code à l'ancienne: sans tests, sans documentation et avec de longues fonctions incompréhensibles. Bonne chance!