
Salut, habrozhiteli! Ce livre rĂ©vise sĂ©rieusement l'essence et les principes de la programmation orientĂ©e objet (POO) et peut ĂȘtre mĂ©taphoriquement appelĂ© «POO Lobachevsky».
Egor Bugaenko , développeur avec 20 ans d'expérience, analyse de maniÚre critique les dogmes de la POO et propose de regarder ce paradigme d'une maniÚre complÚtement nouvelle. Ainsi, il stigmatise les méthodes statiques, les getters, les setters, les méthodes mutables, croyant que c'est mal. Pour un programmeur débutant, ce volume peut devenir un éclaircissement ou un choc, et pour un programmeur expérimenté, c'est une lecture obligatoire.
Extrait «N'utilisez pas de méthodes statiques»
Ah, les mĂ©thodes statiques ... Un de mes sujets prĂ©fĂ©rĂ©s. Il m'a fallu plusieurs annĂ©es pour rĂ©aliser l'importance de ce problĂšme. Maintenant, je regrette tout le temps que j'ai passĂ© Ă Ă©crire des logiciels procĂ©duraux plutĂŽt que des objets. J'Ă©tais aveugle, mais maintenant j'ai vu. Les mĂ©thodes statiques sont tout aussi importantes sinon mĂȘme un problĂšme plus important dans la POO que d'avoir une constante NULL. Les mĂ©thodes statiques, en principe, n'auraient pas dĂ» ĂȘtre en Java et dans d'autres langages orientĂ©s objet, mais, hĂ©las, elles existent. Nous ne devrions pas connaĂźtre des choses telles que le mot-clĂ© statique en Java, mais, hĂ©las, sont forcĂ©s .. Je ne sais pas qui les a amenĂ©s Ă Java, mais ils sont du mal absolu .. Les mĂ©thodes statiques, pas les auteurs de cette fonctionnalitĂ©. Je l'espĂšre.
Voyons quelles sont les mĂ©thodes statiques et pourquoi nous les crĂ©ons toujours. Disons que j'ai besoin de la fonctionnalitĂ© de chargement d'une page Web via des requĂȘtes HTTP. Je crĂ©e une telle «classe»:
class WebPage { public static String read(String uri) {
Il est trĂšs pratique de l'utiliser:
String html = WebPage.read("http://www.java.com");
La méthode read () appartient à la classe de méthodes à laquelle je m'oppose. Je suggÚre d'utiliser un objet à la place (j'ai également changé le nom de la méthode conformément aux recommandations de la section 2.4):
class WebPage { private final String uri; public String content() {
Voici comment l'utiliser:
String html = new WebPage("http://www.java.com") .content();
Vous pouvez dire qu'il n'y a pas beaucoup de différence entre eux. Les méthodes statiques fonctionnent encore plus rapidement, car nous n'avons pas besoin de créer un nouvel objet chaque fois que nous devons télécharger une page Web. Appelez simplement la méthode statique, elle fera le travail, vous obtiendrez le résultat et vous continuerez à travailler. Il n'est pas nécessaire de jouer avec les objets et le garbage collector. De plus, nous pouvons regrouper plusieurs méthodes statiques dans une classe utilitaire et la nommer, par exemple, WebUtils.
Ces mĂ©thodes vous aideront Ă charger des pages Web, Ă obtenir des informations statistiques, Ă dĂ©terminer le temps de rĂ©ponse, etc. Elles comporteront de nombreuses mĂ©thodes et leur utilisation est simple et intuitive. De plus, la façon d'appliquer des mĂ©thodes statiques est Ă©galement intuitive. Tout le monde comprend comment ils fonctionnent. Ăcrivez simplement WebPage.read (), et - vous l'avez devinĂ©! - la page sera lue. Nous avons donnĂ© des instructions Ă l'ordinateur, et il l'exĂ©cute. Simple et clair, non? Et non!
Les mĂ©thodes statiques dans n'importe quel contexte sont un indicateur indubitable d'un mauvais programmeur qui n'a aucune idĂ©e de la POO. Il n'y a aucune justification pour appliquer des mĂ©thodes statiques dans n'importe quelle situation. Prendre soin de la performance ne compte pas. Les mĂ©thodes statiques sont une moquerie d'un paradigme orientĂ© objet. Ils existent en Java, Ruby, C ++, PHP et d'autres langages. Malheureusement Nous ne pouvons pas les jeter, nous ne pouvons pas réécrire toutes les bibliothĂšques open source pleines de mĂ©thodes statiques, mais nous pouvons arrĂȘter de les utiliser dans notre code.
Nous devons cesser d'utiliser des méthodes statiques.
Maintenant, regardons-les de plusieurs positions différentes et discutons de leurs lacunes pratiques. Je peux vous les généraliser à l'avance: les méthodes statiques dégradent la maintenabilité du logiciel. Cela ne devrait pas vous surprendre. Tout se résume à la maintenabilité.
Objective versus Computer Thinking
Initialement, j'ai appelĂ© cette sous-section Objective versus Procedural Thinking, mais plus tard je l'ai renommĂ©e. «PensĂ©e procĂ©durale» signifie presque la mĂȘme chose, mais l'expression «penser comme un ordinateur» dĂ©crit mieux le problĂšme. Nous avons hĂ©ritĂ© de cette façon de penser des premiers langages de programmation tels que Assembly, C, COBOL, Basic, Pascal et bien d'autres. La base du paradigme est que l'ordinateur fonctionne pour nous, et nous lui disons quoi faire, en lui donnant des instructions explicites, par exemple:
CMP AX, BX JNAE greater MOV CX, BX RET greater: MOV CX, AX RET
Il s'agit d'une «routine» d'assembleur pour le processeur Intel 8086. Elle trouve et renvoie le plus grand des deux nombres. Nous les mettons respectivement dans les registres AX et BX, et le rĂ©sultat tombe dans le registre CX. Voici exactement le mĂȘme code C:
int max(int a, int b) { if (a > b) { return a; } return b; }
"Qu'est-ce qui ne va pas avec ça?" - demandez-vous. Rien .. Tout va bien avec ce code - il fonctionne comme il se doit. C'est ainsi que tous les ordinateurs fonctionnent. Ils s'attendent à ce que nous leur donnions des instructions à suivre les uns aprÚs les autres. Pendant de nombreuses années, nous avons écrit des programmes de cette façon. L'avantage de cette approche est que nous restons proches du processeur, dirigeant son mouvement ultérieur. Nous sommes aux commandes et l'ordinateur suit nos instructions. Nous indiquons à l'ordinateur comment trouver le plus grand des deux nombres. Nous prenons des décisions, il les suit. Le flux d'exécution est toujours cohérent, du début du script à sa fin.
Ce type de pensée linéaire est appelé «penser comme un ordinateur». L'ordinateur à un moment donné commence à exécuter des instructions et à un moment donné le termine. Lors de l'écriture de code en C, nous sommes obligés de penser de cette façon. Les opérateurs séparés par des points-virgules vont de haut en bas. Ce style est hérité de l'assembleur.
Bien que les langages d'un niveau plus élevé que l'assembleur aient des procédures, des sous-programmes et d'autres mécanismes d'abstraction, ils n'éliminent pas une façon de penser cohérente. Le programme continue de fonctionner de haut en bas. Il n'y a rien de mal à une telle approche lors de l'écriture de petits programmes, mais à plus grande échelle, il est difficile de penser comme ça.
Jetez un Ćil au mĂȘme code Ă©crit dans le langage de programmation fonctionnel Lisp:
(defun max (ab) (if (> ab) ab))
Pouvez-vous dire oĂč commence et se termine l'exĂ©cution de ce code? Non. Nous ne savons pas comment le processeur obtiendra le rĂ©sultat, ni comment la fonction if fonctionnera. Nous sommes trĂšs Ă©loignĂ©s du processeur. Nous pensons comme une fonction, pas comme un ordinateur. Lorsque nous avons besoin d'une nouvelle chose, nous la dĂ©finissons:
(def x (max 5 9))
Nous dĂ©finissons, ne donnons pas d'instructions au processeur. Avec cette ligne, nous lions x Ă (max 5 9). Nous ne demandons pas Ă l'ordinateur de calculer le plus grand des deux nombres. Nous disons simplement que x est le plus grand des deux nombres. Nous ne contrĂŽlons pas comment et quand il sera calculĂ©. Notez que cela est important: x est le plus grand des nombres. La relation «est» («ĂȘtre», «ĂȘtre») est la diffĂ©rence entre un paradigme de programmation fonctionnel, logique et orientĂ© objet d'un paradigme procĂ©dural.
Avec un état d'esprit informatique, nous sommes à la barre et contrÎlons le flux d'instructions. Avec une façon de penser orientée objet, nous déterminons simplement qui est qui et nous les laissons interagir quand ils en ont besoin. Voici à quoi devrait ressembler le calcul du plus grand des deux nombres dans la POO:
class Max implements Number { private final Number a; private final Number b; public Max(Number left, Number right) { this.a = left; this.b = right; } }
Et donc je vais l'utiliser:
Number x = new Max(5, 9);
Ăcoutez, je ne calcule pas le plus grand des deux nombres. Je dĂ©termine que x est le plus grand des deux nombres. Peu m'importe ce qui se trouve Ă l'intĂ©rieur de l'objet de la classe Max et comment exactement il implĂ©mente l'interface numĂ©rique. Je ne donne pas d'instructions au processeur concernant ce calcul. J'instancie juste l'objet. C'est trĂšs similaire Ă def en Lisp .. En ce sens, la POO est trĂšs similaire Ă la programmation fonctionnelle.
En revanche, les mĂ©thodes statiques en POO sont les mĂȘmes que les sous-routines en C ou assembleur. Ils ne sont pas liĂ©s Ă la POO et nous obligent Ă Ă©crire du code procĂ©dural dans une syntaxe orientĂ©e objet. Voici le code Java:
int x = Math.max(5, 9);
C'est complĂštement faux et ne doit pas ĂȘtre utilisĂ© dans une conception orientĂ©e objet rĂ©elle.
Style déclaratif versus impératif
La programmation impérative «décrit les calculs en termes d'opérateurs qui changent l'état d'un programme». La programmation déclarative, quant à elle, «exprime la logique du calcul sans décrire le flux de son exécution» (je cite Wikipedia). En fait, nous en avons parlé au cours de plusieurs pages précédentes. La programmation impérative est similaire à ce que font les ordinateurs - exécution séquentielle d'instructions. La programmation déclarative est plus proche de la façon naturelle de penser dans laquelle nous avons des entités et des relations entre elles. De toute évidence, la programmation déclarative est une approche plus puissante, mais une approche impérative est plus compréhensible pour les programmeurs procéduraux. Pourquoi l'approche déclarative est-elle plus puissante? Ne changez pas, et aprÚs quelques pages, nous arrivons au point.
Qu'est-ce que tout cela a à voir avec les méthodes statiques? Peu importe qu'il s'agisse d'une méthode ou d'un objet statique, nous devons encore écrire if (a> b) quelque part, non? Oui, exactement. La méthode statique et l'objet ne sont qu'un wrapper sur l'instruction if, qui effectue la tùche de comparaison de a avec b. La différence réside dans la façon dont cette fonctionnalité est utilisée par d'autres classes, objets et méthodes. Et c'est une différence significative. Considérez-le avec un exemple.
Disons que j'ai un intervalle limité à deux entiers, et un entier qui devrait tomber dedans. Je dois m'assurer qu'il l'est. Voici ce que je dois faire si la méthode max () est statique:
public static int between(int l, int r, int x) { return Math.min(Math.max(l, x), r); }
Nous devons créer une autre méthode statique, entre (), qui utilise les deux méthodes statiques disponibles, Math.min () et Math.max (). Il n'y a qu'une seule façon de le faire - une approche impérative, car la valeur est calculée immédiatement. Lorsque je fais un appel, j'obtiens immédiatement le résultat:
int y = Math.between(5, 9, 13);
J'obtiens le numéro 9 juste aprÚs avoir appelé entre (). Une fois l'appel effectué, mon processeur commencera immédiatement à travailler sur ce calcul. Il s'agit d'une approche impérative. Et à quoi ressemble l'approche déclarative?
Ici, jetez un oeil:
class Between implements Number { private final Number num; Between(Number left, Number right, Number x) { this.num = new Min(new Max(left, x), right); } @Override public int intValue() { return this.num.intValue(); } }
Voici comment je vais l'utiliser:
Number y = new Between(5, 9, 13);
Sentez-vous la diffĂ©rence? Elle est extrĂȘmement importante. Ce style sera dĂ©claratif, car je ne dis pas au processeur que les calculs doivent ĂȘtre effectuĂ©s immĂ©diatement. Je viens de dĂ©terminer ce que c'Ă©tait et de laisser Ă l'utilisateur le soin de dĂ©cider quand (et s'il Ă©tait nĂ©cessaire) de calculer la variable y Ă l'aide de la mĂ©thode intValue (). Peut-ĂȘtre qu'il ne sera jamais calculĂ© et mon processeur ne saura jamais quel est le nombre 9. Tout ce que j'ai fait, c'est dĂ©clarer ce que c'est. Je viens d'annoncer. Je n'ai pas encore donnĂ© de travail au processeur. Comme indiquĂ© dans la dĂ©finition, exprimĂ© la logique sans dĂ©crire le processus.
J'entends déjà : «D'accord, je vous comprends. Il existe deux approches - déclarative et procédurale, mais pourquoi la premiÚre est-elle meilleure que la seconde? » J'ai mentionné plus tÎt qu'il est évident que l'approche déclarative est plus puissante, mais n'a pas expliqué pourquoi. Maintenant que nous avons examiné les deux approches avec des exemples, nous allons discuter des avantages d'une approche déclarative.
PremiÚrement, c'est plus rapide. à premiÚre vue, cela peut sembler plus lent. Mais si vous regardez de plus prÚs, il deviendra clair qu'en fait, c'est plus rapide, car l'optimisation des performances est entiÚrement entre nos mains. En effet, il faudra plus de temps pour créer une instance de la classe Between que d'appeler la méthode static between (), au moins dans la plupart des langages de programmation disponibles au moment de la rédaction de ce livre. J'espÚre vraiment que nous aurons un langage dans un futur proche dans lequel l'instanciation d'un objet sera aussi rapide que l'appel d'une méthode. Mais nous ne sommes pas encore venus à lui. C'est pourquoi l'approche déclarative est plus lente ... lorsque le chemin d'exécution est simple et direct.
Si nous parlons d'un simple appel à une méthode statique, alors ce sera certainement plus rapide que de créer une instance d'un objet et d'appeler ses méthodes. Mais si nous avons de nombreuses méthodes statiques, elles seront appelées séquentiellement lors de la résolution du problÚme, et pas seulement pour travailler sur les résultats dont nous avons vraiment besoin. Que diriez-vous de ceci:
public void doIt() { int x = Math.between(5, 9, 13); if () { System.out.println("x=" + x); } }
Dans cet exemple, nous calculons x, que nous ayons besoin de sa valeur ou non. Le processeur trouvera la valeur 9 dans les deux cas. La méthode suivante utilisant l'approche déclarative fonctionnera-t-elle aussi vite que la précédente?
public void doIt() { Integer x = new Between(5, 9, 13); if () { System.out.println("x=" + x); } }
Je pense que le code dĂ©claratif sera plus rapide. C'est mieux optimisĂ©. Et il ne dit pas au processeur quoi faire. Au contraire, il permet au processeur de dĂ©cider quand et oĂč le rĂ©sultat est vraiment nĂ©cessaire - les calculs sont effectuĂ©s Ă la demande.
L'essentiel est que l'approche dĂ©clarative est plus rapide car elle est optimale. C'est le premier argument en faveur d'une approche dĂ©clarative par rapport Ă l'impĂ©ratif de la programmation orientĂ©e objet. Le style impĂ©ratif n'a dĂ©finitivement pas sa place dans la POO, et la premiĂšre raison en est l'optimisation des performances. Il ne faut pas dire que plus vous contrĂŽlez l'optimisation du code, plus il est suivi. Au lieu de laisser l'optimisation du processus de calcul au compilateur, Ă la machine virtuelle ou au processeur, nous le faisons nous-mĂȘmes.
Le deuxiÚme argument est le polymorphisme. En termes simples, le polymorphisme est la capacité de briser les dépendances entre les blocs de code. Supposons que je veuille changer l'algorithme pour déterminer si un nombre tombe dans un certain intervalle. C'est assez primitif en soi, mais je veux le changer. Je ne veux pas utiliser les classes Max et Min. Et je veux qu'il effectue une comparaison en utilisant les instructions if-then-else .. Voici comment procéder de maniÚre déclarative:
class Between implements Number { private final Number num; Between(int left, int right, int x) { this(new Min(new Max(left, x), right)); } Between(Number number) { this.num = number; } }
Il s'agit de la mĂȘme classe Between que dans l'exemple prĂ©cĂ©dent, mais avec un constructeur supplĂ©mentaire. Maintenant, je peux l'utiliser avec un autre algorithme:
Integer x = new Between( new IntegerWithMyOwnAlgorithm(5, 9, 13) );
Ce n'est probablement pas le meilleur exemple, car la classe Between est trĂšs primitive, mais j'espĂšre que vous comprenez ce que je veux dire. La classe Between est trĂšs facile Ă sĂ©parer des classes Max et Min, car ce sont des classes. Dans la programmation orientĂ©e objet, un objet est un citoyen Ă part entiĂšre, mais pas la mĂ©thode statique. Nous pouvons passer l'objet comme argument au constructeur, mais nous ne pouvons pas faire de mĂȘme avec la mĂ©thode statique. Dans la POO, les objets sont associĂ©s Ă des objets, communiquent avec des objets et Ă©changent des donnĂ©es avec eux. Pour dĂ©tacher complĂštement un objet des autres objets, nous devons nous assurer qu'il n'utilise le nouvel opĂ©rateur dans aucune de ses mĂ©thodes (voir section 3.6), ainsi que dans le constructeur principal.
Permettez-moi de répéter: pour détacher complÚtement un objet des autres objets, il vous suffit de vous assurer que le nouvel opérateur n'est utilisé dans aucune de ses méthodes, y compris le constructeur principal.
Pouvez-vous faire le mĂȘme dĂ©couplage et refactoring avec un extrait de code impĂ©ratif?
int y = Math.between(5, 9, 13);
Non, tu ne peux pas. La méthode statique entre () utilise deux méthodes statiques, min () et max (), et vous ne pouvez rien faire avant de l'avoir complÚtement réécrite. Et comment pouvez-vous le réécrire? Passer le quatriÚme paramÚtre à la nouvelle méthode statique?
Ă quel point sera-t-il laid? Je pense vraiment.
Voici mon deuxiÚme argument en faveur d'un style de programmation déclaratif - il réduit la cohésion des objets et le rend trÚs élégant. Sans oublier le fait que moins de cohésion signifie plus de maintenabilité.
Le troisiĂšme argument en faveur de la supĂ©rioritĂ© de l'approche dĂ©clarative sur l'impĂ©ratif - l'approche dĂ©clarative parle des rĂ©sultats, alors que l'impĂ©ratif explique la seule façon de les obtenir. La seconde approche est beaucoup moins intuitive que la premiĂšre. Je dois d'abord «exĂ©cuter» le code dans ma tĂȘte pour comprendre quel rĂ©sultat attendre. Voici une approche impĂ©rative:
Collection<Integer> evens = new LinkedList<>(); for (int number : numbers) { if (number % 2 == 0) { evens.add(number); } }
Pour comprendre ce que fait ce code, je dois le parcourir, visualiser ce cycle. En fait, je dois faire ce que fait le processeur - parcourir tout le tableau des nombres et mettre les nombres pairs dans la nouvelle liste. Voici le mĂȘme algorithme, Ă©crit dans un style dĂ©claratif:
Collection<Integer> evens = new Filtered( numbers, new Predicate<Integer>() { @Override public boolean suitable(Integer number) { return number % 2 == 0; } } );
Ce morceau de code est beaucoup plus proche de la langue anglaise que le précédent. Il se lit comme suit: "evens est une collection filtrée qui inclut uniquement les éléments qui sont pairs." Je ne sais pas exactement comment la classe Filtered crée une collection - utilise-t-elle une instruction for ou autre chose. Tout ce que j'ai besoin de savoir en lisant ce code, c'est que la collection a été filtrée. Les détails de l'implémentation sont masqués et le comportement est exprimé.
Je me rends compte que pour certains lecteurs de ce livre, il était plus facile de percevoir le premier fragment. Il est un peu plus court et trÚs similaire à ce que vous voyez quotidiennement dans le code que vous traitez. Je vous assure que c'est une question d'habitude. C'est un sentiment trompeur. Commencez à penser en termes d'objets et de leur comportement, plutÎt qu'en termes d'algorithmes et leur exécution, et vous obtiendrez une véritable perception. Le style déclaratif se rapporte directement aux objets et à leur comportement, et l'impératif - aux algorithmes et à leur exécution.
Si vous trouvez ce code moche, essayez Groovy, par exemple:
def evens = new Filtered( numbers, { Integer number -> number % 2 == 0 } );
Le quatriĂšme argument est l'intĂ©gritĂ© du code. Jetez un autre coup d'Ćil aux deux extraits prĂ©cĂ©dents. Veuillez noter que dans le deuxiĂšme fragment, nous dĂ©clarons evens comme un seul opĂ©rateur - evens = Filtered (...). Cela signifie que toutes les lignes de code responsables du calcul de cette collection sont cĂŽte Ă cĂŽte et ne peuvent pas ĂȘtre sĂ©parĂ©es par erreur. Au contraire, dans le premier fragment, il n'y a pas de «collage» Ă©vident des lignes. Vous pouvez facilement modifier leur ordre par erreur et l'algorithme se cassera.
Dans un morceau de code aussi simple, c'est un petit problĂšme, car l'algorithme est Ă©vident. Mais si le fragment du code impĂ©ratif est plus grand - disons, 50 lignes, il peut ĂȘtre difficile de comprendre quelles lignes de code sont liĂ©es les unes aux autres .. Nous avons discutĂ© un peu plus tĂŽt du problĂšme de la concatĂ©nation temporelle - lors de la discussion des objets immuables .. Le style de programmation dĂ©clarative aide Ă©galement Ă Ă©liminer cette concatĂ©nation , grĂące Ă laquelle la maintenabilitĂ© est amĂ©liorĂ©e.
Il y a probablement encore des arguments, mais j'ai citĂ© le plus important, de mon point de vue, de ceux liĂ©s Ă la POO. J'espĂšre avoir pu vous convaincre que le style dĂ©claratif est ce dont vous avez besoin. Certains d'entre vous peuvent dire: «Oui, je comprends ce que vous voulez dire. Je combinerai les approches dĂ©claratives et impĂ©ratives, le cas Ă©chĂ©ant. J'utiliserai des objets lĂ oĂč cela a du sens et des mĂ©thodes statiques quand je devrai faire rapidement quelque chose de simple, comme calculer le plus grand des deux nombres. ".." Non, tu te trompes! " - Je vais te rĂ©pondre. Vous ne devez pas les combiner. N'utilisez jamais un style impĂ©ratif. Ce n'est pas un dogme. Cela a une explication trĂšs pragmatique.
Le style impĂ©ratif ne peut pas ĂȘtre combinĂ© avec un caractĂšre dĂ©claratif purement technique. , â .
, â max() min(). , . , , .. â Between, between(). ? , , , , . . , Between. , - , .
- : , â . .
« ! â . â ?» ⊠, . - , - ( ). , , â . , .. , . , , â , , , . , Apache Commons FileUtils.readLines(), . :
class FileLines implements Iterable<String> { private final File file; public Iterator<String> iterator() { return Arrays.asList( FileUtils.readLines(this.file) ).iterator(); } }
, , :
Iterable<String> lines = new FileLines(f)
FileLines, . . , , â FileLines. , .
-
- , , ( -).. , java.lang.Math â -. Java, Ruby , , . ? . 1.1 , â . - , :
class Math { private Math() { // } public static int max(int a, int b) { if (a < b) { return b; } return a; } }
, -, , , . , , , .
- â - . - â â . , , . - â .. .
«»
«» â , , . , , . :
class Math { private static Math INSTANCE = new Math(); private Math() {} public static Math getInstance() { return Math.INSTANCE; } public int max(int a, int b) { if (a < b) { return b; } return a; } }
. Math, INSTANCE.. , getInstance(). , . INSTANCE â getInstance().
«» , . , . , . , , , , -, . - Math, , :
class Math { private Math() {} public static int max(int a, int b) { if (a < b) { return b; } return a; } }
max():
Math.max(5, 9);
? , , . , -? Java-. , : « ». Par exemple:
class User { private static User INSTANCE = new User(); private String name; private User() {} public static User getInstance() { return User.INSTANCE; } public String getName() { return this.name; } public String setName(String txt) { this.name = txt; } }
, . «, ». -, , - . .. , -: « ».. . .. -, , :
class User { private static String name; private User() {} public static String getName() { return User.name; } public static String setName(String txt) { User.name = txt; } }
- , . , ? ? , â , , - â , . , , setInstance() getInstance(). , . , :
Math.getInstance().max(5, 9);
Math. , Math â , . , Math , . , . , , , -, . , , Math.max() -. ? :
Math math = new FakeMath(); Math.setInstance(math);
«» , . : - , . - â . - â â .
, ? -, , . Pourquoi? , â , , .. . . , :
#include <stdio> int line = 0; void echo(char* text) { printf("[%d] %s\n", ++line, text); }
echo(), line. line *.-.. . Java , . Java, Ruby --, . Pourquoi? . . . . , . , , GOTO.
, , - Java, «».. - , . .
. .
« ? â . â , , ?» , , , . - . ? !
, .
, , .. . . , . , : , , . . , , , . , â , 2.1.
. .
: , ()? , , , .. , ? Lisp, Clojure Haskell Java C++?
, :
class Max implements Number { private final int a; private final int b; public Max(int left, int right) { this.a = left; this.b = right; } @Override public int intValue() { return this.a > this.b ? this.a : this.b; } }
:
Number x = new Max(5, 9);
Lisp , :
(defn max (ab) (if (> ab) ab))
, ? Lisp .
, , â . - , - -, . , - Java, , Java , -. â , . .
, - . -, Java, ( ) , . .
, . â - . â - , â , , :
names = new Sorted( new Unique( new Capitalized( new Replaced( new FileNames( new Directory( "/var/users/*.xml" ) ), "([^.]+)\\.xml", "$1" ) ) ) )
, , -. , 3.2. , names, , , . , , , . .
? , , , .
, . Directory, FileNames, Replaced, Capitalized, Unique Sorted â , . . .
, ( ). , Unique â Iterable, . FileNames â , .
- . , .. - app.run(), . if, for, switch while. , .
if Java , . Java , If? :
float rate; if (client.age() > 65){ rate = 2.5; } else { rate = 3.0; }
- :
float rate = new If( client.age() > 65, 2.5, 3.0 );
?
float rate = new If( new Greater(client.age(), 65), 2.5, 3.0 );
, :
float rate = new If( new GreaterThan( new AgeOf(client), 65 ), 2.5, 3.0 )
- . â , rate.
, , . if, for, switch while. If, For, Switch While. ?
, . . . , .. , .
, - â .
? , : . , . . . , â .
: static â , , .
»Plus d'informations sur le livre sont disponibles sur
le site Web de l'Ă©diteur20% â
Java