2010-08-12 8 views
9

Ok, je n'essaie pas de lancer une guerre de flammes ici, et je sais que l'argument entre les langages statiques et dynamiques a été banni plusieurs fois, y compris ici. Mais j'ai une question très pratique que j'espère que quelqu'un ici pourra éclaircir. Désolé pour la longueur, mais ce n'est pas une question simple avec probablement pas une réponse simple. Ruby, PHP, et Javascript sont des langages très populaires ces jours-ci, et ils ont beaucoup de gens qui les défendent et soutiennent qu'être dynamiquement typé ne retient pas le développeur. Je suis novice dans ces langues et je voudrais commencer à les utiliser pour des projets plus importants, mais voici un scénario de refactoring de base qui revient tout le temps au travail (work == C#), et je me demande quelle serait l'approche dans Ruby - J'ai choisi Ruby parce que c'est OO. Ok, j'utilise Ruby, et je construis un objet Client. Il a des méthodes pour charger/sauvegarder/supprimer de la base de données. C'est bon et les gens l'utilisent. J'ajoute plus de méthodes pour d'autres choses et les gens l'utilisent plus. J'ajoute une méthode pour calculer l'historique des commandes en fonction de certains paramètres. A présent, cette classe est utilisée dans tout le système. Puis, un jour, je décide de changer les paramètres de la méthode GetOrderHistory. Alors je:Refactoring avec un langage typé dynamiquement

  • ajouter les nouveaux paramètres à la méthode
  • Réécrire le code de la méthode à utiliser les nouveaux paramètres
  • modifier le code client j'avais à l'esprit pour passer les nouveaux paramètres et utiliser cette modifiés méthode

Mais maintenant quoi? J'ai des dizaines/centaines/whoknows combien d'autres endroits dans le système qui ont besoin d'être changé. Dans un langage OO dynamique comme Ruby ou Javascript, comment pourrais-je m'y prendre?

Du haut de ma tête, ne sachant pas très bien Ruby, je peux penser à deux réponses stupides:

  1. 100% de couverture de code. Je teste l'application entière et chaque fois qu'elle casse, je vois si c'est cette méthode et la corrige
  2. Rechercher et remplacer. J'utilise la recherche de texte pour rechercher cette méthode. Mais je pourrais avoir d'autres objets avec les mêmes noms de méthodes.

Alors, y a-t-il une bonne réponse? Il semble que l'IDE aurait du mal. Si j'avais un code tel que

c = Customer.new 

il serait en mesure de le comprendre, mais s'il est

c= SomeFunctionThatProbablyReturnsACustomerButMightReturnOtherThings() 

Alors, quelle approche vous experts Ruby prendre dans ce cas?

+0

Je suis assez sûr que le refactoring ne devrait pas casser les contrats existants ... – CurtainDog

+0

@CurtainDog qu'en est-il de renommer une fonction privée dans une bibliothèque? – Toskan

+0

un autre bon exemple est ruby ​​et python. Puis-je vous rappeler amicalement, que ni ruby, ni python, s'en tenir à ce que vous venez de dire lors de la sortie de nouvelles versions? – Toskan

Répondre

3

L'un des arguments forts que vous allez entendre est que vous devriez écrire des tests au préalable. Ceci, en théorie, vous montrerait exactement où l'application doit changer au cas où quelque chose d'autre changerait.

Mais ce n'est que le sommet de l'iceberg. Ruby est conçu avec certaines lignes directrices à l'esprit, comme les fonctions courtes et expressives, la répartition des responsabilités dans les modules, la non-répétition du code (DRY), le principe de la moindre surprise, etc. plus un ensemble de pratiques recommandées, comme tester d'abord, passer des paramètres comme options de hachage, utiliser la métaprogrammation à bon escient, etc. Je suis sûr que d'autres langages dynamiques le font aussi.

Si c n'est pas un client, alors au moins je m'attendrais à agir comme un. Les IDE peuvent rechercher le typage de canard, ce qui est plus flexible que de rechercher une instance d'une classe particulière.

Certains IDE (au moins Rubymine) recherchent également des conventions. Par exemple, dans les applications Rails, Rubymine accède au fichier de schéma et ajoute les propriétés du modèle dans la base de données en tant que méthodes. Il reconnaît également les associations (has_many, belongs_to, etc.) et ajoute dynamiquement les méthodes correspondantes, que Rails génère sous le capot. Maintenant, cela réduit à peu près le besoin de refactoring, au moins le garder au minimum. Mais certainement ne le résout pas. Et je ne pense pas que cela puisse être résolu.

+0

bonnes idées, merci. L'idée de typer le canard est particulièrement intéressante, je vais y regarder de plus près. En outre, dans un autre article, quelqu'un a parlé de Flow Analysis, qui est, à mon avis, une précompilation plus intelligente, capable de comprendre ce que sera le type d'exécution. Cela semble compliqué si ... – LoveMeSomeCode

+1

aussi, ne pas être argumentatif, mais l'idée de tests me dérange. Pas les tests eux-mêmes, parce que je crois que tout bon code devrait avoir des tests unitaires approfondis, mais l'idée de la verbosité. Beaucoup de gens diront que les langages dynamiques sont moins verbeux parce que vous n'avez pas besoin de descripteurs de type, mais quand les gens parlent de sécurité, ils mentionnent le besoin de tests supplémentaires, ce qui annule le code concis qu'ils ont écrit . juste une observation générale ... – LoveMeSomeCode

+0

Eh bien, je suis désolé ... mais pourriez-vous, et je suis sérieux, argumenter que ces concepts devraient s'appliquer dans tous les langages de programmation? Ne pas pouvoir refactoriser est en effet un gros problème. – Toskan

1

Ce ne sera probablement pas la meilleure réponse à votre question, mais j'ai tendance à aimer concevoir des méthodes pour accepter un hachage pour couvrir les changements futurs.

Exemple:

def my_method(options = {}) 
    if options[:name] 
    ... 
    end 
end 

Je pense que beaucoup de gens les plus avancés Ruby chercherait à mettre en œuvre une sorte de modèle Métaprogrammation.

D'autres options peuvent inclure le dépassement de la méthode dans une sous-classe pour exécuter la fonctionnalité que vous désirez.

Ou, que diriez-vous ...

def get_order_history(required_param, options = []) 

    @required_param = required_param 

    if options[:do_something_else] 
    result = other_method(options[:do_something_else]) 
    else 
    result = ... 
    end 

    result  

end 

def other_method(something_else) 
    ... 
end 
+0

Le sous-classement est un bon point. C'est l'une des options que j'ai envisagées après avoir posté, mais je me suis dit: «Et si la méthode change vraiment, comme en raison d'un changement de schéma db, et que vous n'avez pas du tout besoin de l'ancien code? utilise la nouvelle méthode? – LoveMeSomeCode