Petit exercice

Pour un entretien (que j'ai raté), il y a eu ce petit exercice à  faire via un éditeur en ligne et en 20 minutes.


 


J'ai fait a peu près la moitié, je serais curieux de voir ceux qui y arrive :) (ou si je suis nul ^^)



/* * Technical Exercise Task
 * 
 * Duration: 25 minutes
 * 
 * 1. Create an Objective-C class to represent IBAN numbers with these requirements:
 *   a. The name of the Class must be Iban.
 * b. It must be immutable.
 * c. It has a designated initializer called -initWithString:.
 * d. It must support multiple countries, for example:
 * i. GR16 0110 1250 0000 0001 2300 695
 * ii. CH93 0076 2011 6238 5295 7
 *   e. Unit test at least one of your implemented methods
 *   f. You can consult this Wikipedia article for reference: http://en.wikipedia.org/wiki/International_Bank_Account_Number
 * 
 * 2. Sort the list of Iban objects (given in the exercise) in lexicographical order
 * 
 * 3. Print the sorted list in 2. with nicely formatted strings (in 4-by-4 groups of characters)
 * 
 */


#import <Foundation/Foundation.h>
#include <assert.h>




void test () {
    @autoreleasepool {
       
      Iban * ibanA = [[Iban alloc] initWithString:@GR16 0110 1250 0000 0001 2300 695];
      Iban * ibanB = [[Iban alloc] initWithString:@AA10 0382 0320 0110 3201 0300 371];
      Iban * ibanC = [[Iban alloc] initWithString:@GR160110125000000001 2300695];
      
      // 1.e. Unit test at least one of your implemented methods
      // ...




    }
}


void run () {
    @autoreleasepool {
   
      NSSet * ibans = [NSSet setWithObjects:
                        [[Iban alloc] initWithString:@GR16 0110 1250 0000 0001 2300 695],
                        [[Iban alloc] initWithString:@GB29NWBK60161331926819],
                        [[Iban alloc] initWithString:@SA03 8000 0000 6080 1016 7519],
                        [[Iban alloc] initWithString:@CH9300762011623852957],
                        [[Iban alloc] initWithString:@NO2345328734114],
                        [[Iban alloc] initWithString:@DE44 6452 1111 33432 7346 44],
                        [[Iban alloc] initWithString:@CH46 0054 7373 6333 383 22],
                      nil];     
      
      // 2. Sort the list of Iban objects (given in the exercise) in lexicographical order
      // ...
      
      
      // 3. Print the sorted list in 2. with nicely formatted strings (in 4-by-4 groups of characters)
      // ...
      
      
    }
}


int main (int argc, const char * argv[]) {
  
  @autoreleasepool {
    
    // Test
    test();
    
    // Run
    run();
    
  }
}

«1

Réponses

  • Joanna CarterJoanna Carter Membre, Modérateur
    janvier 2015 modifié #2
    En vingt minutes ? Avec les tests ? Sans Xcode ? Ouah ! C'est lourd !


    C'est ton code ? Si oui, où se trouve la classe ?
  • Tes vraiment loin d'être nul ! Tu m'as deja souvent rendu service lors de tes nombreux conseils ou réponses a des membres
  • Moi aussi j'ai raté mon test professionnel au service militaire, il y a fort longtemps. A vrai dire je n'ai pas réussi à  répondre à  une seule question en une heure !

    Bon faut dire aussi que le test portait sur l'électronique des tubes à  vide, une technologie obsolète avant même ma naissance. C'est marrant ces tests dépassés que des générations d'examinateurs persistent à  utiliser, malgré l'évolution de la technologie.
  • AliGatorAliGator Membre, Modérateur
    Faire cet exercice en entier (et en plus dans un éditeur en ligne qui ne permet ni de vérifier la syntaxe ni de faire de l'autocompletion) est quasi-impossible dans le temps imparti de 25 minutes... si tu lis entre les lignes et pense qu'ils demandent à  ce que ta classe sache vérifier la validité de l'IBAN passé en paramètre du constructeur.


    S'ils s'attendent effectivement à  ce que la classe fasse la vérification que la longueur du BBAN soit valide en fonction du code du pays (ce qui veut dire connaà®tre la longueur et le format pour tous les pays connus ayant adopté l'IBAN, en plus !), ou qu'elle vérifie que le modulo 97 de sécurité est correct (alors que faire un modulo sur un nombre aussi grand n'est pas standard vu la taille de l'entier à  utiliser lorsqu'on suit l'algo), alors c'est du boulot qui ne tient pas dans les 25 minutes.


    Sauf que moi si tu me mets devant un problème comme ça je fais du pur KISS et YAGNI : je ne fais strictement QUE ce qui est demandé. D'autant que si tu veux faire plus, il te manquerait des informations dans l'énoncé (ex. : si l'IBAN fourni dans le constructeur est un IBAN invalide, faut-il renvoyer un objet nil, ou bien renvoyer une instance de IBAN non-nil et prévoir éventuellement une méthode "isValid", et si oui cest méthode ne pourrait-elle pas alors indiquer au passage le type d'erreur tant qu'on y est ? etc). Mais tout ceci n'est pas spécifié ni demandé dans l'énoncé malgré ce que tu peux avoir envie de faire en lisant l'énoncé.


    Et je me demande si ce n'est pas d'ailleurs un des buts de l'exercice : vérifier que tu es plutôt du genre TDD / KISS / YAGNI à  n'implementer que le strict necessaire sans te lancer dans du code inutile du moins qui n'est pas necessaire d'après les spécifications, ou si tu es du genre à  te lancer tête baissée dans une classe qui gère tout alors que ce n'est pas demandé.

    C'est exactement l'exemple (volontairement exagéré) que j'ai déjà  évoqué sur le forum d'un énoncé demandant de faire une methode qui retourne YES quandon a gagné. L'énoncé ne demande pas que la methode retourne NO si on a perdu après tout, donc faire une méthode qui retourne YES systématiquement répond parfaitement à  l'énoncé. Si l'examinateur (le client) n'est pas content et réalise qu'en fait il voulait aussi que ça retourne NO si on a perdu, c'est de sa faute il avait qu'à  mieux spécifier son besoin.




    Donc moi perso en face d'un tel exercice je ferais juste une classe qui a un constructeur qui prend une chaà®ne de longueur arbitraire et qui sait présenter cette chaà®ne par groupe de 4 caractères. Tu rajoutes la méthode "compare:" à  cette classe pour pouvoir satisfaire le besoin n°2 et c'est fini.
  • C'était 20 mn, pas 25 :)


     


    J'ai fait comme toi, la classe avait une property iban, et une méthode formatiBan qui virait les espaces vides puis collait un espace tous les 4 caractères.


     


    Par contre j'ai pas eu le temps de faire le sorting du NSSet.


     


    T'aurais fait ca comment avec le compare: ?


  • AliGatorAliGator Membre, Modérateur
    janvier 2015 modifié #7
    Bah l'énoncé indique 25 minutes pas 20.


    Pour le tri c'est pas compliqué :
    @implementation Iban
    ...
    - (NSComparisonResult)compare:(Iban*)other
    {
    return [self.ibanString caseInsensitiveCompare:other.ibanString];
    }
    @end
    Où ibanString est la property de la class Iban qui stocke la chaà®ne sans espace (tu auras pris soin d'enlever les espaces de la chaine passée en paramètre dans le constructeur avant de le mémoriser dans la propriété)


    Ceci dit je dois t'avouer que je n'avais pas vu que c'était un NSSet (je suis sur mon iPhone ce n'est pas pratique à  lire le code sur un écran pas large ^^). Du coup maintenant que j'ai déjà  implémenté "compare:" au dessus je peux toujours faire "[ibans.allObjects sortedArrayUsingSelector:@selector(compare:)]" pour avoir le tableau trié.


    Mais à  vrai dire il n'y a même pas besoin de ça ni de cette méthode compare: tu n'as rien du tout à  implémenter, le tri est déjà  disponible gratuitement sans rien rajouter. Enfin si, il faut quand même du coup exposer la propriété "ibanString" en readonly (car l'énoncé demande que la classe soit immutable) dans son @interface. Mais dès que tu as accès en lecture à  cette propriété ibanString le tri se fait en 1-2 lignes :
    NSSortDescriptor* desc = [NSSortDescriptor sortDescriptorWithKey:@ibanString ascending:YES];
    NSArray* sortedList = [ibans sortedArrayUsingDescriptors:@[;desc]];
    Et voilà .
  • AliGatorAliGator Membre, Modérateur
    janvier 2015 modifié #8
    Au final ma réponse à  l'exercice :
    @interface Iban : NSObject
    - (instancetype)initWithString:(NSString*)ibanString;
    @property(nonatomic, readonly) NSString ibanString;
    @end
    @implementation Iban
    - (instancetype)initWithString:(NSString*)ibanString
    {
    self = [super init];
    if (self)
    {
    _ibanString = [ibanString stringByReplacingOccurencesOfString:@ withString:@""];
    }
    return self;
    }

    - (NSString*)description
    {
    NSMutableString* str = [NSMutableString new];
    for(size_t i=0; i<_ibanString.length; ++i)
    {
    [str appendString:[self substringWithRange:NSMakeRange(i,1)]];
    if (i%4 == 0) [str appendString:@ ];
    }
    return [str copy];
    }
    @end
    Ce n'est peut-être pas le code le plus optimal ni le plus optimisé, il ne traite pas forcement tous les cas qu'on pourrait rencontrer dans le futur ni ne fait beaucoup de trucs supplémentaires dont on pourrait avoir besoin plus tard, mais après tout il fait exactement ce qui est demandé et je te défie de me montrer qu'il ne respecte pas les contraintes demandées par l'énoncé ou qu'il ne marche pas avec les exemples fournis ;)
  • AliGatorAliGator Membre, Modérateur
    janvier 2015 modifié #9
    Et pour compléter le code à  trous du cou :

    #import <Foundation/Foundation.h>
    #include <assert.h>

    void test () {
    @autoreleasepool {

    Iban * ibanA = [[Iban alloc] initWithString:@GR16 0110 1250 0000 0001 2300 695];
    Iban * ibanB = [[Iban alloc] initWithString:@AA10 0382 0320 0110 3201 0300 371];
    Iban * ibanC = [[Iban alloc] initWithString:@GR160110125000000001 2300695];

    // 1.e. Unit test at least one of your implemented methods
    assert([[ibanA description] isEqualToString:@GR16 0110 1250 0000 0001 2300 695]);
    assert([[ibanB description] isEqualToString:@AA10 0382 0320 0110 3201 0300 371]);
    assert([[ibanC description] isEqualToString:@GR16 0110 1250 0000 0001 2300 695]);
    }
    }


    void run () {
    @autoreleasepool {

    NSSet * ibans = [NSSet setWithObjects:
    [[Iban alloc] initWithString:@GR16 0110 1250 0000 0001 2300 695],
    [[Iban alloc] initWithString:@GB29NWBK60161331926819],
    [[Iban alloc] initWithString:@SA03 8000 0000 6080 1016 7519],
    [[Iban alloc] initWithString:@CH9300762011623852957],
    [[Iban alloc] initWithString:@NO2345328734114],
    [[Iban alloc] initWithString:@DE44 6452 1111 33432 7346 44],
    [[Iban alloc] initWithString:@CH46 0054 7373 6333 383 22],
    nil];

    // 2. Sort the list of Iban objects (given in the exercise) in lexicographical order
    NSSortDescriptor* sd = [[NSSortDescriptor alloc] initWithKey:@ibanString ascending:YES];
    NSArray sortedIbans = [ibans sortedArrayUsingDescriptors:@[;sd]];


    // 3. Print the sorted list in 2. with nicely formatted strings (in 4-by-4 groups of characters)
    NSLog(@%@", sortedIbans); // Will call description on each Iban object which will thus be formatted nicely.

    }
    }
  • LarmeLarme Membre
    janvier 2015 modifié #10

    @Ali :

    Dans l'overwrite de description :

    ça ne serait pas mieux de créer le str via le self.ibanString (et non pas self) ainsi que de virer au départ tous les espaces qu'il pourrait contenir ?

     

    Un truc de ce genre :

     



    NSMutableString* str = [NSMutableString stringWithString:[ibanString stringByReplacingOccurrencesOfString:@ withString:@""]];

    Edit : Ou mieux, de virer les espace dans initWithString:.


  • AliGatorAliGator Membre, Modérateur

    @Ali :

    Dans l'overwrite de description :

    ça ne serait pas mieux de créer le str via le self.ibanString (et non pas self) ainsi que de virer au départ tous les espaces qu'il pourrait contenir ?

     

    Un truc de ce genre :

     

    NSMutableString* str = [NSMutableString stringWithString:[ibanString stringByReplacingOccurrencesOfString:@  withString:@""]];
    J'ai corrigé mon code immédiatement après l'avoir posté ce n'est plus le cas (mais comme je tape depuis mon iPad " et Dieu que c'est chiant de taper du code sur un iPad qui n'arrête pas d'auto corriger le texte ! " et qu'à  chaque fois que j'édite un de mes post pour le modifier depuis l'iPad, il m'enlève TOUS les sauts à  la ligne et que je suis obligé de les réinsérer systématiquement au milieu du gros pâté de texte qu'il me fait, bah l'édition juste pour corriger ça a pris un peu de temps ^^ C'est d'ailleurs pour cela que je ne corrige pas mon "du cou" en "du coup" dans mon post précédent, ni ne corrige les quelques fautes de frappe de mon message explicatif YAGNI encore plus haut, car sur iPad il m'enlève tous les sauts de ligne et j'ai pas envie de tous me les retaper juste pour ça :P)


  •  et je te défie de me montrer qu'il ne respecte pas les contraintes demandées par l'énoncé ou qu'il ne marche pas avec les exemples fournis  ;)



     


    c. It has a designated initializer called -initWithString:.



    - (instancetype)initWithString:(NSString*)ibanString

    Cette ligne n'indique pas que c'est un designated initialized :).



    - (instancetype)initWithString:(NSString*)ibanString NS_DESIGNATED_INITIALIZER;

  • comment sais-tu que tu as raté l'entretien ?


  • J'ai eu la réponse de la boite ;)


  • @ali : t'as prit combien de temps du coup ? 20 mn ?


  • Moi je n'aurais écrit que le .h de la classe (après tout, il n'est pas demandé d'écrire tout le code mais juste de "créer une classe"), ce qui m'aurait permis quand même de montrer que :


    -j'ai compris 2 ou 3 trucs sur l'IBAN en 5 minutes passées sur l'article wikipedia (j'ai des capacités de synthèse),


    -je connais vraiment objective-c puisque je sais écrire avec les bonnes conventions un @interface.


    -je sais ce que veut dire immutable (avec le readonly indiqué par Ali)


    -je sais ce que veut dire tests unitaires.


     


    Après j'aurais été un peu coincé parce que je ne connais pas par coeur les méthode dispo sur un NSSet. Je me doute bien qu'il y a un truc qui permet de faire un tri avec un block ou avec un key mais de là  à  connaitre la syntaxe sans aide ni autocompletion...


     


    Après j'aurais fait un for sur le NSArray ou le NSOrderedSet en appelant une méthode formattedIban.


     


    Personnellement, je n'aurais pas vu la demande sur le designated interface mais je ne suis pas sur que ce soit un mal.


     


    Plus grave, je n'étais pas certain de ce que voulait dire "lexicographical", je pensais bien que c'était alphabétique mais peut-être avec des subtilités que je ne connaissais pas...


     


     


    Faut aussi savoir quel est le poste visé.


    Si c'est un poste de chef de projet ou de lead developer, ce n'est pas forcément à  toi d'écrire le code mais ton rôle est plutôt de bien comprendre le besoin et de bien le traduire vers l'équipe de développement. Donc dans cette optique, bien synthétiser ce que tu as compris de l'IBAN dans une liste de méthodes que tu souhaites obtenir de la part des développeurs. et surtout pas rentrer dans le detail de comment on valide un IBAN selon les pays etc.


     


    Ne pas oublier que le but de l'entretien c'est de donner envie pas juste de résoudre un exercice. En tant que "bête à  concours", on a parfois tendance à  trop se focaliser sur le côté "examen".

  • Y'a pas de .h ni de .m, tu devais tout écrire dans le meme fichier ;)




  • J'ai eu la réponse de la boite ;)




     


    Dommage pour eux. 

  • FKDEVFKDEV Membre
    janvier 2015 modifié #19


    Y'a pas de .h ni de .m, tu devais tout écrire dans le meme fichier ;)




     


    J'aurais mis des //TODO dans certaines méthodes, genre



    -(bool)isValid
    {
        //TODO check validity according to spec 
        return YES;
    }

    Ou alors, il cherchait vraiment un mec capable d'écrire un truc complet en 20 minutes.


     


    La solution de Ali me plait qu'à  moitié car oui, elle répond à  l'exercice mais elle ne démontre pas grand chose. Si tu vas faire une classe pour gérer un Iban, tu vas forcément avoir besoin d'une méthode isValid.




  •  



     


    c. It has a designated initializer called -initWithString:.



    - (instancetype)initWithString:(NSString*)ibanString

    Cette ligne n'indique pas que c'est un designated initialized :).



    - (instancetype)initWithString:(NSString*)ibanString NS_DESIGNATED_INITIALIZER;


     




    Intéressant ce machin.

    J'ai regardé vite fait et je vais essayer de le garder en tête.

  • je trouve quand meme l'exo pas mal :


    1- tu sais crée un objet


    2- tu sais crée un initializer


    3- tu sais ce qu'est immutable (le readonly j'y ai pensé 2 mn après avoir rendu l'ex -_- alors que je le savais)


    4- tu sais manipuler une chaine et faire un algo simple


    5- tu sais tester avec assert


    6- tu connais NSSet et le tri (bon ce point je le trouve foireux, perso j'utilise essentiellement dictionnaires et array, donc je connaissais pas les méthodes qu'il faut)


     


    En 20 mn ca permet quand meme de voir de façon globale ton niveau (je trouve en tout cas)


  • 7- T'as des capacités de synthèse.


    8- Tu sais gérer ton temps.


    9- T'es pas non plus un geek complet qui sait même ce qu'est NS_DESIGNATED_INITIALIZER  >:D


  • exactement ^^


  • Ce que je trouve le plus complexe dans cet exercice c'est tout ce qui touche à  NSSet. On a pas forcément en tête toutes les méthodes disponibles.


  • Dans l'absolu ca n'a rien de compliqué (meme regarder la doc de NSSet) mais en 20mn faut effectivement tout savoir en amont !


  • Au sujet de l'ordre lexicographique, je l'avais compris aussi comme étant l'ordre alphabétique. Je crois que c'est la bonne manière de le comprendre, enfin il me semble.


  • J'ai pas honte de le dire. Je serais clairement incapable de le faire en 20 min ( je ne suis même pas sur de le faire en moins de 40 ).


    Je n'ai clairement pas en tête les méthodes de NSSet et j'ai besoin de temps pour poser un problème.


  • AliGatorAliGator Membre, Modérateur


    @ali : t'as prit combien de temps du coup ? 20 mn ?

    J'ai pas chronométré mais je dirais 10mn à  écrire le code " mais sur iPad qui me faisait de l'autocorrection que je ne voulais pas et qu'il fallait que je reprenne régulièrement " et avant 5-10mn à  prendre connaissance du sujet et savoir ce que j'allais faire. En tout pas loin de 20 mn mais devant un ordinateur plutôt qu'un iPad j'aurais été plus rapide.
  • AliGatorAliGator Membre, Modérateur
    janvier 2015 modifié #29


    J'aurais mis des //TODO dans certaines méthodes, genre


    -(bool)isValid
    {
    //TODO check validity according to spec
    return YES;
    }
    Alors d'une part c'est BOOL et pas bool (ce sont 2 types proches mais differents) et d'autre part c'est pas demandé. Donc justement according to quelles specs ?

    Ou alors, il cherchait vraiment un mec capable d'écrire un truc complet en 20 minutes.

    Ce n'est clairement pas possible dans ce délai si tu fais comme tu proposes d'implémenter plus de choses que ce qui est strictement demandé ; car dans ce cas c'est un travail de spécification (et limite d'architecture) pour savoir de quelles fonctionnalités tu auras besoin dans ta classe Iban (pourquoi pas une méthode qui donne le pays associé à  l'IBAN ? Et une méthode pour donner le validityCheck etc... c'est un travail de réflexion qui prend un peu de temps sans parler du travail d'architecture pour savoir quelle serait la meilleure façon d'écrire l'API. Rien que ça peut à  lui tout seul prendre les 20mn (voire plus). Alors que ce n'est pas demandé.

    La solution de Ali me plait qu'à  moitié car oui, elle répond à  l'exercice mais elle ne démontre pas grand chose. Si tu vas faire une classe pour gérer un Iban, tu vas forcément avoir besoin d'une méthode isValid.

    Qu'en sais-tu ? ça dépend clairement du besoin et du contexte dans lequel tu vas avoir à  utiliser cette classe. Si ça se trouve cette classe va servir à  manipuler des IBAN issus d'un Web Service qui se charge déjà  de la validation en amont donc aucun besoin de le réimplémenter dans ta classe.


    Et sinon ma solution montre énormément de choses contrairement à  ce que tu affirmes. Elle démontre que le candidat sait lire correctement un énoncé, ne perd pas de temps à  écrire du code inutile et respecte des principes clés et importants comme YAGNI etc.


    Pour moi c'est d'ailleurs clairement le but de l'exercice. Si le temps imparti était de 2h ça ne serait pas la même chose et le même but, certainement ; si le temps imparti était toujours 20mn mais le sujet moins complet et plus simple non plus. Mais là  vu le contexte à  demander un tel sujet en si peu de temps l'objectif est clairement de savoir si tu sais aller à  l'essentiel et rester KISS et YAGNI & co, bref à  pas être du genre à  faire faire de choses inutiles et sur implémenter les choses par rapport aux spécifications " ce qui est un mal de plus en plus répandu et qui fait pas mal de dégâts dans les sociétés de prestation qui ont des délais de plus en plus serrés à  tenir.


  • Alors d'une part c'est BOOL et pas bool (ce sont 2 types proches mais differents) et d'autre part c'est pas demandé. Donc justement according to quelles specs ?


    Et sinon ma solution montre énormément de choses contrairement à  ce que tu affirmes. Elle démontre que le candidat sait lire correctement un énoncé, ne perd pas de temps à  écrire du code inutile et respecte des principes clés et importants comme YAGNI etc.




     


     


    "bool", oups c'est du C#.


    Oui, j'ai un peu exagéré en disant que tu ne démontres "pas grand chose".


     


    Cependant, attention quand même, avec les specs et YAGNI.


    En tant que professionnel, tu as un devoir de conseil envers ton client.


    Si celui-ci ne pense pas à  te demander un contrôle de validité sur l'IBAN tu dois quand même le faire ou au moins lui proposer puisque ce n'est pas trivial et aura une influence sur le coût.


    Cela fait partie des exigences implicites qu'il faut faire expliciter assez vite pour éviter les malentendus.


     


    Là  dans le contexte de l'exercice, je ne pense pas que cela fasse de mal d'évoquer la possibilité d'avoir une méthode isValid, une méthode interne pour parser l'IBAN, etc.


     Le but c'est quand même de se faire embaucher.


    Une fois que tu as montré que tu connais vraiment Objective-C, c'est bien de montrer que tu as compris la problématique et que tu es capable de concevoir une mini-classe avec ses interfaces, sans pour autant tomber dans le hors-sujet bien sûr.


     


     


    D'autant qu'il y a quand même un point auquel je ne sais pas comment répondre autrement qu'en renseignant un peu sur l'interface et le fonctionnement interne:



     * d. It must support multiple countries, for example:
     * i. GR16 0110 1250 0000 0001 2300 695
     * ii. CH93 0076 2011 6238 5295 7
  • Précision : la partie sur le support des différents pays, le mec m'avait dit que c'était pas important


Connectez-vous ou Inscrivez-vous pour répondre.