Simple : type Enumeration ?

MachaonMachaon Membre
novembre 2017 modifié dans API UIKit #1

Bonsoir à  tous,


 


une question toute bête :


1/ j'ai décrit une énumération enum


2/ Je veux créer une variable simple qui contiendra plus tard mon énumération (ou une autre) et je ne sais pas quel type lui donner.


 


Merci



enum Actions : String, Codable {
case Lire = "Lire"
case Chanter = "Chanter"
case Jouer = "Jouer"
case Taper = "Taper"

static let allValues = [Lire, Chanter, Jouer, Taper]
}

enum Temps : String, Codable {
case TLJ = "TLJ"
case SuiteALaPrecedente = "Suite"
case j1 = "1j"
case j2 = "2j"
case j3 = "3j"
case j4 = "4j"

static let allValues = [TLJ, SuiteALaPrecedente, j1, j2, j3, j4]
}


Plus tard...



let itemsActions = Actions.allValues
let itemsTemps = Temps.allValues

(...)


var items : ??????!!!!!!
if (collectionView.restorationIdentifier == "Actions") {
items = itemsActions
} else if (collectionView.restorationIdentifier == "Temps"){
items = itemsTemps
}


Mots clés:

Réponses

  • Bonsoir Machaon !


     


    C'est très simple : une enum, c'est un type !



    let itemsActions : Actions = .allValues
    let itempsTemps : Temps = .allValues

    D'ailleurs, lorsque tu voudras déclarer ou modifier une variable dont le type est une enum, tu auras l'auto complétion dès que tu auras mis le point.



  • Bonsoir Machaon !


     


    C'est très simple : une enum, c'est un type !



    let itemsActions : Actions = .allValues
    let itempsTemps : Temps = .allValues

    D'ailleurs, lorsque tu voudras déclarer ou modifier une variable dont le type est une enum, tu auras l'auto complétion dès que tu auras mis le point.




     


    Merci pour ta réponse Luc-A.


    Je comprends donc que je ne peux pas affecter à  items soit l'un soit l'autre... c'est dommage, je vais devoir dupliquer plus de code dans mon if.


     


    :)

  • Je t'en prie !


     


    Peux-tu être plus précis ?


     


    Si tu dois utiliser une condition, tu devrais utiliser Switch plutôt que if !



    switch {
    case .Lire:
    instructions...
    case .Chanter:
    instructions...
    case .Jouer:
    instructions...
    case .Taper:
    instructions...
    case .none:
    instructions...
    }

    Pour la case .none, c'est une nouveauté de Swift 4.


    Je veux bien t'aider, mais il faut en dire plus !




  •  


    Je comprends donc que je ne peux pas affecter à  items soit l'un soit l'autre... c'est dommage, je vais devoir dupliquer plus de code dans mon if.


     




     


    Dommage c'est vite dis .. Swift est un langage très strict sur les types de données. On ne peut pas mélanger des données de nature différente, ce qui est une bonne chose pour la sécurité des applications. 


     


    Effectivement on ne peut pas faire certaines bidouilles comme d'autres langages plus anciens, ce qui élimine un risque de bugs par effet de bord. Ce n'est pas grave d'avoir quelques lignes de code en plus. Ce qui compte c'est la lisibilité du code, la facilité de maintenance (les modifications ultérieures), la réutilisation des objets et la sécurité.

  • CéroceCéroce Membre, Modérateur
    novembre 2017 modifié #6

    Je comprends donc que je ne peux pas affecter à  items soit l'un soit l'autre... c'est dommage, je vais devoir dupliquer plus de code dans mon if.

    On peut évidemment créer des enums composées:
     
    enum Item {
    case action (Action)
    case temps (Temps)
    }

    var items = [Item.action(.Chanter), Item.temps(.j1)]

    P.S.: Quand une enum hérite de String, il n'est pas nécessaire de définir les chaà®nes:
    enum Action : String {
    case Lire
    case Chanter
    case Jouer
    case Taper
    }
    P.S. 2: Les canons de codage en Swift recommandent que les cases commencent par une minuscule.
  • JérémyJérémy Membre
    novembre 2017 modifié #7


    On peut évidemment créer des enums composées:




     


    Dans le même type d'idée, il serait possible de créer une enum ?


    Par exemple :



    enum type {
    let action { get }
    let temps { get }
    }

    et l'utiliser de cette façon :



    var items : [Item] = [Action.Chanter, Temps.j1]

  • CéroceCéroce Membre, Modérateur
    Hum, ta question n'est pas très claire... souhaiterais-tu associer une Action et un Temps ?
    Il suffit d'utiliser une struct.
  • JérémyJérémy Membre
    novembre 2017 modifié #9

    J'ai écrit n'importe quoi, je reprends.


     


    On pourrait faire quelque chose similaire avec l'utilisation d'un protocol ?



    protocol Item { }

    enum Actions : String, Codable, Item {
    case Lire = "Lire"
    case Chanter = "Chanter"
    case Jouer = "Jouer"
    case Taper = "Taper"

    static let allValues = [Lire, Chanter, Jouer, Taper]
    }

    enum Temps : String, Codable, Item {
    case TLJ = "TLJ"
    case SuiteALaPrecedente = "Suite"
    case j1 = "1j"
    case j2 = "2j"
    case j3 = "3j"
    case j4 = "4j"

    static let allValues = [TLJ, SuiteALaPrecedente, j1, j2, j3, j4]
    }

    var items: [Item] = [Actions.Lire,Temps.]
  • CéroceCéroce Membre, Modérateur
    Swift requiert que les éléments dans un array soient de même type. Donc, effectivement, rendre Action et Temps conformes à  Item permet de les mettre dans un même array.

    Mais ensuite, on n'est pas très avancé. On se retrouve avec un array d'Items qui ne savent rien faire, puisque le protocole Item est vide.
  • JérémyJérémy Membre
    novembre 2017 modifié #11


    Swift requiert que les éléments dans un array soient de même type. Donc, effectivement, rendre Action et Temps conformes à  Item permet de les mettre dans un même array.


    Mais ensuite, on n'est pas très avancé. On se retrouve avec un array d'Items qui ne savent rien faire, puisque le protocole Item est vide.




     


    Okay, mais si on fait ça ? Ca ne fonctionne pas ? (j'ai pas de playground sous la main)



    protocol Item: String { }

    enum Actions : Codable, Item {
    case Lire = "Lire"
    case Chanter = "Chanter"
    case Jouer = "Jouer"
    case Taper = "Taper"

    static let allValues = [Lire, Chanter, Jouer, Taper]
    }

    enum Temps : Codable, Item {
    case TLJ = "TLJ"
    case SuiteALaPrecedente = "Suite"
    case j1 = "1j"
    case j2 = "2j"
    case j3 = "3j"
    case j4 = "4j"

    static let allValues = [TLJ, SuiteALaPrecedente, j1, j2, j3, j4]
    }

    var items: [item] = [Actions.Lire,Temps.j1,.........]

    for item in items {
    switch item {
    case "Lire":
    // more code
    break
    case "j1":
    // more code
    break
    }
    }

  • CéroceCéroce Membre, Modérateur
    Non, le compilo ne peut pas faire de switch sur un Item puisqu'il ignore que c'est une énumération. Par contre, si le protocole Item déclarait des méthodes, on pourrait les appeler.
  • JérémyJérémy Membre
    novembre 2017 modifié #13

    Oui exact ! La proposition ci-dessous devrait fonctionner.



    protocol Item: CustomStringConvertible { }

    enum Action: Item {
    case lire, chanter, jouer, taper

    var description: String {
    switch self {
    case .lire:
    return "lire"
    case .chanter:
    return "chanter"
    case .jouer:
    return "jouer"
    case .taper:
    return "taper"
    }
    }

    static let all: [Action] = [.lire, .chanter, .jouer, .taper]
    }

    enum Temps: Item {
    case tlj, j1, j2, j3, j4

    var description: String {
    switch self {
    case .tlj:
    return "tlj"
    case .j1:
    return "j1"
    case .j2:
    return "j2"
    case .j3:
    return "j3"
    case .j4:
    return "j4"
    }
    }

    static let all: [Temps] = [.tlj, .j1, .j2, .j3, .j4]
    }

    var items: [Item] = [Action.chanter,Temps.tlj]

    for item in items {
    switch item.description {
    case Action.chanter.description:
    // code
    break
    case Temps.tlj.description:
    // code
    break
    default:
    break
    }
    }

  • DrakenDraken Membre
    novembre 2017 modifié #14

    Et pourquoi pas :



    import UIKit

    enum Action:String {
    case lire
    case chanter
    case jouer
    case taper
    }

    enum Temps:String {
    case tlj
    case j1
    case j2
    case j3
    case j4
    }

    class ViewController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()

    let items = [Action.chanter.rawValue, Temps.tlj.rawValue]

    for item in items {
    switch item {
    case Action.chanter.rawValue:
    print ("->chanter")
    break
    case Temps.tlj.rawValue:
    print ("->tlj")
    break
    default:
    break
    }
    }
    }
    }

    C'est plus simple et fonctionne très bien. Je viens de tester.


    Pas besoin de créer un protocol, ni de définir une propriété description.



  • Et pourquoi pas :



    import UIKit

    enum Action:String {
    case lire
    case chanter
    case jouer
    case taper
    }

    enum Temps:String {
    case tlj
    case j1
    case j2
    case j3
    case j4
    }

    class ViewController: UIViewController {

    override func viewDidLoad() {
    super.viewDidLoad()

    let items = [Action.chanter.rawValue, Temps.tlj.rawValue]

    for item in items {
    switch item {
    case Action.chanter.rawValue:
    print ("->chanter")
    break
    case Temps.tlj.rawValue:
    print ("->tlj")
    break
    default:
    break
    }
    }
    }
    }

    C'est plus simple et fonctionne très bien. Je viens de tester.




     


    Oui oui oui. J'aime bien utiliser les protocol pour "normaliser" le code. Je suis assez fana de la programmation par contrat. Mais effectivement tu peux t'en passer. Et ta solution est plutôt intéressante. 

  • Joanna CarterJoanna Carter Membre, Modérateur

    Ce code avec tous les switch est bien moche.



    protocol Instructions
    {
    func instruction1()

    func instruction2()
    }


    enum Actions : String, Codable
    {
    case lire = "Lire"
    case chanter = "Chanter"
    case jouer = "Jouer"
    case taper = "Taper"

    static let allValues = [lire, chanter, jouer, taper]
    }

    extension Actions : Instructions
    {
    func instruction1()
    {
    print("instruction1 \(self.rawValue)")
    }

    func instruction2()
    {
    print("instruction2 \(self.rawValue)")
    }
    }


    enum Temps : String, Codable
    {
    case tlj = "TLJ"
    case suiteALaPrecedente = "Suite"
    case j1 = "1j"
    case j2 = "2j"
    case j3 = "3j"
    case j4 = "4j"

    static let allValues = [tlj, suiteALaPrecedente, j1, j2, j3, j4]
    }

    extension Temps : Instructions
    {
    func instruction1()
    {
    print("instruction1 \(self.rawValue)")
    }

    func instruction2()
    {
    print("instruction2 \(self.rawValue)")
    }
    }


    override func viewDidLoad()
    {
    super.viewDidLoad()

    let items: [Instructions] = [Actions.lire, Temps.j1]

    for item in items
    {
    item.instruction1()

    item.instruction2()
    }
    }
  • JérémyJérémy Membre
    novembre 2017 modifié #17


     


    Ce code avec tous les switch est bien moche.




     


    Bah heu.... ça dépend de ce qu'on a envie de faire...  :/


     


    Dans ton cas tu es obligé de déclarer instrusction1 et instruction2 dans tes deux énums même si elle servent à  rien. Je ne dis pas que ça ne fonctionne pas (quoi que...) mais je ne crois pas que ce soit forcément une meilleur idée que la proposition de Draken.


     


    D'ailleur, si pour l'enum Actions.lire tu dois appeler uniquement l'instruction 2 et pour Temps.j1 uniquement l'instruction 1, ça fonctionne ? Bah non.   


  • Joanna CarterJoanna Carter Membre, Modérateur

    Les switch sont toujours moches  :-*


     


    Normalement, en ajoutant plusieurs objets dans un liste, on pourrait vouloir faire la même chose avec tous, n'importe quoi le type.


     


    À la place d'un protocol vide, on peux l'utiliser pour tenir la/les méthode(s) commune(s). Mon exemple n'était qu'un exemple ; on n'est pas obligé d'avoir deux méthodes.


     


    Et on peut avoir plusieurs protocols pour grouper les comportements.


  • JérémyJérémy Membre
    novembre 2017 modifié #19

    Non mais ma remarque était pour pinailler ! 


     


    Tu as quoi contre les switchs ? Pour les énums je les trouve plutôt pratiques.  :)


  • Joanna CarterJoanna Carter Membre, Modérateur
    novembre 2017 modifié #20


    Non mais ma remarque était pour pinailler ! 




     


    ::)


     




    Tu as quoi contre les switchs ? Pour les énums je les trouve plutôt pratiques.  :)




     


    Dans un enum, peut-être mais ailleurs, je préfère d'utiliser l'équivalent du "Visitor Pattern" i.e. en Swift, les méthodes d'un protocol implémentée dans une extension.




  • je préfère d'utiliser l'équivalent du "Visitor Pattern"




     


    Fais nous une petite présentation de ce design pattern ! 

  • Joanna CarterJoanna Carter Membre, Modérateur
    novembre 2017 modifié #22

    L'idée derrière le "Visitor Pattern" est que l'on puisse ajouter des fonctionnalités à  une hiérarchie de types sans devant modifier les types originaux.


     


    Typiquement, avec la plupart des langages POO, à  part de Swift, on trouverait :



    protocol Visitor { }

    protocol Visitable
    {
    func accept(visitor: Visitor)
    }

    class DrawingVisitor : Visitor
    {
    func drawCircle(_ circle: Circle)
    {
    print("draw one side")
    }

    func drawSquare(_ square: Square)
    {
    print("draw four sides")
    }

    func drawTriangle(_ triangle: Triangle)
    {
    print("draw three sides")
    }
    }

    class Shape : Visitable
    {
    var origin = CGPoint.zero

    var size = CGSize.zero

    var numberOfSides: Int
    {
    return 0
    }

    func accept(visitor: Visitor) { }
    }

    class Circle : Shape
    {
    override var numberOfSides: Int
    {
    return 1
    }

    override func accept(visitor: Visitor)
    {
    if let visitor = visitor as? DrawingVisitor
    {
    visitor.drawCircle(self)
    }
    }
    }

    class Square : Shape
    {
    override var numberOfSides: Int
    {
    return 4
    }

    override func accept(visitor: Visitor)
    {
    if let visitor = visitor as? DrawingVisitor
    {
    visitor.drawSquare(self)
    }
    }
    }

    class Triangle : Shape
    {
    override var numberOfSides: Int
    {
    return 3
    }

    override func accept(visitor: Visitor)
    {
    if let visitor = visitor as? DrawingVisitor
    {
    visitor.drawTriangle(self)
    }
    }
    }

    Mais, avec Swift et ses Protocols et ses Extensions, on n'est plus obligé de créer les hiérarchies de classes ; on peut ajouter les fonctionnalités  plus facilement :



    protocol Shape
    {
    var origin: CGPoint { get set }

    var size: CGSize { get set }

    var numberOfSides: Int { get }
    }

    protocol Drawable
    {
    func draw()
    }

    struct Circle : Shape
    {
    var origin: CGPoint

    var size: CGSize

    let numberOfSides = 1
    }

    extension Circle : Drawable
    {
    func draw()
    {
    print("draw one side")
    }
    }

    struct Square : Shape
    {
    var origin: CGPoint

    var size: CGSize

    let numberOfSides = 4
    }

    extension Square : Drawable
    {
    func draw()
    {
    print("draw four sides")
    }
    }

    struct Triangle : Shape
    {
    var origin: CGPoint

    var size: CGSize

    let numberOfSides = 3
    }

    extension Triangle : Drawable
    {
    func draw()
    {
    print("draw three sides")
    }
    }

    Dans le premier cas, nous avons dû toucher les classes pour déclarer qu'ils sont Visitable et, dans chaque méthode "accept", nous également dû écrire du code pour déterminer le type du Visitor et quelle méthode à  appeler. Mais, le code qui implémente le comportement est toujours dans le Visitor.


     


    Avec Swift, il n'est plus nécessaire de définir les protools Visitor et Visitable, on peut tout simplement ajouter le comportement dans une extension de chaque type dont nous avons besoin.


  • Les extensions c'est le Bien !

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