Je pense à des classes de type que la possibilité d'ajouter sécurisée de type de métadonnées à une classe.
Donc, vous devez d'abord définir une classe pour modéliser le domaine du problème, puis penser à des métadonnées à ajouter. Des choses comme des Égaux, Hashable, Visible, etc. Cela crée une séparation du domaine du problème et les mécaniciens de la classe et s'ouvre sous-classement parce que la classe est plus maigre.
Sauf que, vous pouvez ajouter des classes de type de n'importe où dans la portée, et pas seulement là où la classe est définie et vous pouvez changer implémentations. Par exemple, si je fais le calcul d'un code de hachage pour une classe Point par Point à l'aide de#hashCode, puis je me suis limitée à la mise en œuvre spécifique qui ne peut pas créer une bonne distribution des valeurs pour l'ensemble spécifique de Points que j'ai. Mais si j'utilise Hashable[Point], alors je peut donner mon propre mise en œuvre.
[Mise à jour avec l'exemple]
Comme un exemple, voici un cas d'utilisation, j'ai eu la semaine dernière. Dans notre produit il y a plusieurs cas de Cartes contenant des récipients en tant que valeurs. E. g., Map[Int, List[String]]
ou Map[String, Set[Int]]
. L'ajout de ces collections peuvent être commentée:
map += key -> (value :: map.getOrElse(key, List()))
Donc, je voulais avoir une fonction qui encapsule ce que je pourrais écrire
map +++= key -> value
Le principal problème est que les collections n'ont pas tous les mêmes méthodes pour ajouter des éléments. Certains ont '+', tandis que d'autres:+'. Je voulais aussi conserver l'efficacité de l'ajout d'éléments à une liste, de sorte que je ne voulais pas utiliser de plier/carte de créer de nouvelles collections.
La solution est d'utiliser les classes de type:
trait Addable[C, CC] {
def add(c: C, cc: CC) : CC
def empty: CC
}
object Addable {
implicit def listAddable[A] = new Addable[A, List[A]] {
def empty = Nil
def add(c: A, cc: List[A]) = c :: cc
}
implicit def addableAddable[A, Add](implicit cbf: CanBuildFrom[Add, A, Add]) = new Addable[A, Add] {
def empty = cbf().result
def add(c: A, cc: Add) = (cbf(cc) += c).result
}
}
Ici, j'ai défini une classe de type Addable
qui peut ajouter un élément C pour une collection CC. J'ai 2 implémentations par défaut: Pour les Listes à l'aide de ::
et pour les autres collections, à l'aide du générateur de cadre.
Ensuite, en utilisant ce type de classe est:
class RichCollectionMap[A, C, B[_], M[X, Y] <: collection.Map[X, Y]](map: M[A, B[C]])(implicit adder: Addable[C, B[C]]) {
def updateSeq[That](a: A, c: C)(implicit cbf: CanBuildFrom[M[A, B[C]], (A, B[C]), That]): That = {
val pair = (a -> adder.add(c, map.getOrElse(a, adder.empty) ))
(map + pair).asInstanceOf[That]
}
def +++[That](t: (A, C))(implicit cbf: CanBuildFrom[M[A, B[C]], (A, B[C]), That]): That = updateSeq(t._1, t._2)(cbf)
}
implicit def toRichCollectionMap[A, C, B[_], M[X, Y] <: col
Le bit spécial est à l'aide de adder.add
d'ajouter les éléments et adder.empty
de créer de nouvelles collections pour de nouvelles clés.
Pour comparer, sans les classes de type j'aurais eu 3 options:
1. écrire une méthode par type de collection. E. g., addElementToSubList
et addElementToSet
etc. Cela crée beaucoup de passe-partout dans la mise en œuvre et pollue l'espace de noms
2. pour utiliser la réflexion pour déterminer si la sous-collection est une Liste de / Set. C'est délicat comme la carte est vide pour commencer (bien sûr scala permet ici aussi avec les Manifestes)
3. avoir du pauvre type de la classe en demandant à l'utilisateur pour l'alimentation de l'additionneur. Donc, quelque chose comme addToMap(map, key, value, adder)
, ce qui est laid