classe en langage et type OOP

9

Dans la théorie du langage de programmation, un type est un ensemble de valeurs. Par exemple, le type "int" est l'ensemble de toutes les valeurs entières.

Dans les langages POO, une classe est un type, n'est-ce pas?

Lorsqu'une classe est définie avec plusieurs membres, par exemple

class myclass{
    int a; 
    double b;
}

Quand nous parlons d'une classe, voulons-nous dire

  • " (a,b)aest un int et best un double", ou
  • "{ (x,y)| xest un entier, yest-ce un double}"?

Que signifie une instance de myclass?

  • " (a,b)aest un int et best un double", ou
  • un objet qui occupe un espace mémoire et qui peut (pas nécessairement, c'est-à-dire peut être vide) stocker (x,y), où xest tout int et ytout double?
Tim
la source
2
Une classe est un type. "{(x, y) | x est un entier, y est un double}" " serait presque correct, sauf pour deux choses: 1) vous avez utilisé un tuple alors qu'une classe est conceptuellement un enregistrement - vous vous référez à son champs par nom, pas par position; et 2) Tous les enregistrements avec des champs aet ne bsont pas membres de ce type, comme le mentionne Killian Forth. Myclass est isomorphe aux enregistrements avec des champs aet bde type intet double- vous pouvez prendre un enregistrement comme celui-ci et le transformer en une instance de myclass.
Doval
1
Dans les langues fortement typées, une classe est un type. Dans les langues faiblement typées, il peut ou non s'agir d'un type.
shawnhcorey
1
Dans la théorie du langage de programmation, un type est un ensemble de valeurs? Je pense que vous devez vous procurer un autre livre ou un autre professeur ou les deux. Une «variable» ou une «constante» a un «type» et a souvent une «valeur». Il existe des types de valeurs nulles, des scalaires et des types de valeurs composites où la valeur de la variable ou de la constante contient des sous-variables / sous-constantes.
user1703394
1
@ user1703394 Un type est un ensemble de valeurs. Un type entier 32 bits est un ensemble de 2 ^ 32 valeurs distinctes. Si une expression est évaluée à une valeur de ce type, vous savez que cette valeur se trouve dans cet ensemble. Les opérateurs mathématiques ne sont que des fonctions sur les valeurs de cet ensemble.
Doval
1
Je serais également prudent en considérant les types comme des ensembles de valeurs. Les ensembles ont des relations qui ne sont pas strictement applicables aux types. Pour conceptualiser les types, c'est un bon modèle, mais il tombe en panne une fois que vous commencez à regarder les choses de plus près - et même plus lorsque vous introduisez le sous-typage.
Telastyn

Réponses:

30

Ni.

Je suppose que vous demandez si le fait d'avoir le même ensemble de types de champs suffit à classer comme étant la même classe, ou s'ils doivent également être nommés de manière identique. La réponse est: "Ne pas même avoir les mêmes types et les mêmes noms suffit!" Les classes structurellement équivalentes ne sont pas nécessairement compatibles avec les types.

Par exemple, si vous avez un CartesianCoordinateset une PolarCordinatesclasse, ils pourraient tous deux avoir deux nombres comme champs, et ils pourraient même avoir le même Numbertype et les mêmes noms, mais ils ne seraient toujours pas compatibles, et une instance de PolarCoordinatesne serait pas un instance de CartesianCoordinates. La possibilité de séparer les types en fonction de leur objectif et non de leur implémentation actuelle est un élément très utile pour écrire du code plus sûr et plus facile à gérer.

Kilian Foth
la source
9
Il convient de noter que dans certaines langues étant structurellement équivalent est suffisant pour faire un type un sous - type de l'autre (et souvent vice - versa). Mais cela est décidément peu commun / impopulaire.
Telastyn
7
@Tim Un typedef ne crée pas de type, il alias le nom utilisé pour faire référence à un type existant.
Doval
1
@DevSolar Il a explicitement mentionné C, et à part C ++, je ne connais aucun autre langage qui utilise ce mot-clé.
Doval
3
@Telastyn - ces langues devraient être tuées par le feu.
Jon Story
4
@JonStory Le sous-typage structurel est utile au niveau du module; le manque est ce qui vous oblige à tout transformer interfaceen Java et C # si jamais vous voulez faire des tests unitaires. Vous finissez par écrire une tonne de passe-partout juste pour pouvoir changer la classe particulière que votre programme utilisera même si vous n'avez pas l'intention de la changer au moment de l'exécution.
Doval
6

Les types ne sont pas des ensembles.

Vous voyez, la théorie des ensembles a un certain nombre de fonctionnalités qui ne s'appliquent tout simplement pas aux types, et vice-versa . Par exemple, un objet a un seul type canonique. Il peut s'agir d'une instance de plusieurs types différents, mais un seul de ces types a été utilisé pour l'instancier. La théorie des ensembles n'a aucune notion d'ensembles "canoniques".

La théorie des ensembles vous permet de créer des sous-ensembles à la volée , si vous avez une règle qui décrit ce qui appartient au sous-ensemble. La théorie des types ne le permet généralement pas. Alors que la plupart des langues ont un Numbertype ou quelque chose de similaire, elles n'en ont pasEvenNumber et il ne serait pas simple d'en créer un. Je veux dire, il est assez facile de définir le type lui-même, mais tout Numbers existant qui se trouve même ne sera pas magiquement transformé en EvenNumbers.

En fait, dire que vous pouvez "créer" des sous-ensembles est quelque peu fallacieux, car les ensembles sont un tout autre type d'animal. Dans la théorie des ensembles, ces sous-ensembles existent déjà , de toutes les façons infinies que vous pouvez les définir. Dans la théorie des types, nous nous attendons généralement à traiter un nombre fini (s'il est grand) de types à un moment donné. Les seuls types qui existeraient sont ceux que nous avons réellement définis, pas tous les types que nous pourrions éventuellement définir.

Les ensembles ne sont pas autorisés à se contenir directement ou indirectement . Certains langages, tels que Python, fournissent des types avec des structures moins régulières (en Python, typele type canonique de est typeet objectest considéré comme une instance de object). D'un autre côté, la plupart des langues ne permettent pas aux types définis par l'utilisateur de s'engager dans ce genre de tromperie.

Les ensembles sont généralement autorisés à se chevaucher sans être contenus les uns dans les autres. Ceci est rare dans la théorie des types, bien que certains langages le prennent en charge sous forme d'héritage multiple. D'autres langages, tels que Java, n'autorisent qu'une forme restreinte de cela ou l'interdisent entièrement.

Le type vide existe (il est appelé le type inférieur ), mais la plupart des langues ne le prennent pas en charge ou ne le considèrent pas comme un type de première classe. Le "type qui contient tous les autres types" existe également (il est appelé le type supérieur ) et est largement pris en charge, contrairement à la théorie des ensembles.

NB : Comme certains commentateurs l'ont souligné précédemment (avant que le thread ne soit déplacé vers le chat), il est possible de modéliser des types avec la théorie des ensembles et d'autres constructions mathématiques standard. Par exemple, vous pouvez modéliser l'appartenance à un type sous forme de relation plutôt que modéliser des types sous forme d'ensembles. Mais en pratique, c'est beaucoup plus simple si vous utilisez la théorie des catégories au lieu de la théorie des ensembles. C'est ainsi que Haskell modélise sa théorie des types, par exemple.


La notion de "sous-typage" est vraiment très différente de la notion de "sous-ensemble". Si Xest un sous-type de Y, cela signifie que nous pouvons remplacer des instances de Ypar des instances de Xet le programme "fonctionnera" dans un certain sens. C'est comportemental plutôt que structurel, bien que certains langages (par exemple Go, Rust, sans doute C) aient choisi ce dernier pour des raisons de commodité, soit pour le programmeur soit pour l'implémentation du langage.

Kevin
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
World Engineer
4

Les types de données algébriques sont le moyen d'en discuter.

Il existe trois façons fondamentales de combiner des types:

  • Produit. C'est essentiellement ce à quoi vous pensez:

    struct IntXDouble{
      int a; 
      double b;
    }
    

    est un type de produit; ses valeurs sont toutes les combinaisons possibles (c'est-à-dire des tuples) de un intet un double. Si vous considérez les types de nombres comme des ensembles, la cardinalité du type de produit est en fait le produit des cardinalités des champs.

  • Somme. Dans les langages procéduraux, c'est un peu gênant à exprimer directement (classiquement, cela se fait avec des unions étiquetées ), donc pour une meilleure compréhension, voici un type de somme dans Haskell:

    data IntOrDouble = AnInt Int
                     | ADouble Double
    

    les valeurs de ce type ont soit la forme AnInt 345, soit ADouble 4.23, mais il n'y a toujours qu'un seul nombre impliqué (contrairement au type de produit, où chaque valeur a deux nombres). Donc la cardinalité: vous énumérez d'abord toutes les Intvaleurs, chacune doit être combinée avec le AnIntconstructeur. De plus , toutes les Doublevaleurs, chacune combinée avec ADouble. D'où le type de somme .

  • Exponentiation 1 . Je ne discuterai pas de cela en détail ici car il n'y a pas du tout de correspondance OO claire.

Et les cours? J'ai délibérément utilisé le mot-clé structplutôt que classpour IntXDouble. Le fait est qu'une classe en tant que type n'est pas vraiment caractérisée par ses champs, ce ne sont que des détails d'implémentation. Le facteur crucial est plutôt de savoir quelles valeurs distinctes la classe peut avoir.

Ce qui est pertinent cependant, c'est que la valeur d'une classe peut être des valeurs de n'importe laquelle de ses sous-classes ! Ainsi, une classe est en fait un type de somme plutôt qu'un type de produit: si Aet Bseraient tous deux dérivés de myClass, la myClassserait essentiellement la somme de Aet B. Quelle que soit la mise en œuvre réelle.


1 Il s'agit de fonctions (au sens mathématique! ); un type de fonction Int -> Doubleest représenté par l'exponentielle DoubleInt. Dommage si votre langue n'a pas de fonctions appropriées ...

à gauche
la source
2
Désolé, mais je pense que c'est une très mauvaise réponse. Fonctions faire un analogue clair OO, à savoir les méthodes (et types d'interface unique méthode). La définition de base d'un objet est qu'il a à la fois un état (champs / membres de données) et un comportement (méthodes / fonctions membres); votre réponse ignore ce dernier.
ruakh
@ruakh: non. Vous pouvez bien sûr implémenter des fonctions en OO, mais en général, les méthodes ne sont pas des fonctions ( car elles modifient l'état, etc.). Les «fonctions» dans les langages procéduraux ne sont pas non plus d'ailleurs des fonctions. En effet, les interfaces à méthode statique unique se rapprochent le plus des types fonction / exponentiel, mais j'espérais éviter de discuter de cela car cela n'a aucune pertinence pour cette question.
leftaroundabout
... plus important encore, ma réponse tient compte du comportement. En effet, le comportement est généralement la raison pour laquelle vous utilisez l'héritage, et l'unification des différents comportements possibles capture précisément l'aspect de type somme des classes OO.
leftaroundabout
@ruakh Une méthode ne peut pas sans son objet. L'analogue le plus proche est les staticméthodes, sauf qu'elles ne sont toujours pas des valeurs de première classe. La chose à propos de la plupart des langages OO est qu'ils prennent les objets comme le plus petit bloc de construction, donc si vous voulez quelque chose de plus petit, vous devez le simuler avec des objets, et vous finissez toujours par glisser dans un tas de fonctions sémantiques. Par exemple, cela n'a pas de sens de comparer des fonctions pour l'égalité, mais vous pouvez toujours comparer deux objets de fausses fonctions.
Doval
@Doval 1) vous pouvez passer des méthodes autour d'AFAIK, donc ce sont des valeurs de première classe; 2) il est logique de comparer les fonctions pour l'égalité, les personnes JS le font tout le temps.
Den
2

Désolé mais je ne connais pas la théorie "brute". Je ne peux que fournir une approche pratique. J'espère que cela est acceptable chez programmers.SE; Je ne connais pas l'étiquette ici.


Un thème central de la POO est la dissimulation d'informations . Ce que sont exactement les données des membres d'une classe ne devrait pas intéresser ses clients. Un client envoie des messages à (appelle des méthodes / fonctions membres de) une instance, qui peut ou non modifier l'état interne. L'idée est que les internes d'une classe peuvent changer, sans que le client en soit affecté.

Un corrolaire à cela est que la classe est responsable de s'assurer que sa représentation interne reste "valide". Supposons une classe qui stocke un numéro de téléphone (simplifié) dans deux entiers:

    int areacode;
    int number;

Ce sont les données membres de la classe. Cependant, la classe sera probablement bien plus que ses membres de données, et elle n'est certainement pas définissable comme "ensemble de toutes les valeurs possibles de int x int". Vous ne devez pas avoir un accès direct aux membres des données.

La construction d'une instance peut refuser tout nombre négatif. Peut-être que la construction normaliserait également le code régional d'une certaine manière, ou même vérifierait le nombre entier. Vous finirez ainsi jusqu'à beaucoup plus proche de votre "(a,b) where a is an int and b is a double", car il est certainement pas tout deux int stocké dans cette classe.

Mais cela n'a pas vraiment d'importance en ce qui concerne la classe. Ce n'est ni le type des membres de données, ni la plage de leurs valeurs possibles qui définit la classe, ce sont les méthodes qui sont définies pour elle.

Tant que ces méthodes restent les mêmes, l'implémenteur pourrait changer les types de données en virgule flottante, BIGNUMs, chaînes, peu importe, et à toutes fins pratiques, ce serait toujours la même classe .


Il existe des modèles de conception pour garantir que de tels changements de représentation interne peuvent être effectués sans que le client n'en soit conscient (par exemple, l'idiome pimpl en C ++, qui cache tous les membres de données derrière un pointeur opaque ).

DevSolar
la source
1
It is neither the type of the data members, nor the range of their possible values that defines the class, it's the methods that are defined for it.Les membres de données ne définissent pas une classe uniquement lorsque vous les masquez. C'est peut-être le cas le plus courant, mais on ne peut certainement pas dire que c'est vrai pour toutes les classes. Si même un seul champ est public, il est tout aussi important que ses méthodes.
Doval
1
Sauf si vous codez en Java, où vous n'avez pas le choix en la matière et même vos faux enregistrements muets sans comportement doivent être classes. (Les marquer finalaide à faire passer le message, mais quand même). protectedCependant, vous avez toujours un problème avec les membres, qui peuvent être hérités et font donc partie d'une deuxième API pour les implémenteurs de sous-classes.
Doval
1
@Doval: J'ai compris qu'il s'agissait d'une question "théorique", c'est pourquoi je suis resté aussi clair que possible sur les problèmes de langage réels. (Tout comme je reste aussi loin de Java et protectedque possible dans la pratique. ;-))
DevSolar
3
Le problème est que a classest une construction dépendante du langage. Pour autant que je sache, la classthéorie des types n'existe pas .
Doval
1
@Doval: Cela ne signifierait-il pas que la théorie des types en soi ne s'applique pas aux classes, car elles sont une construction hors de la portée de cette théorie?
DevSolar
2
  • Un type est une description d'une catégorie / plage de valeurs, de structures composées ou de ce que vous avez. OOPwise, il s'apparente à une "interface". (Dans le sens indépendant de la langue. Le sens spécifique à la langue, pas tellement. En Java, par exemple, intest un type , mais n'a aucun rapport avec un interface. Les spécifications de champ public / protégé, également, ne font pas partie d'un interface, mais font partie d'une "interface" ou d'un type .)

    Le point principal étant, c'est beaucoup plus une définition sémantique que concrète. La structure ne tient compte que dans la mesure où les champs / comportements exposés et leurs objectifs définis s'alignent. Si vous n'avez pas les deux, vous n'avez pas de compatibilité de type.

  • Une classe est la réalisation d'un type. C'est un modèle qui définit réellement la structure interne, le comportement attaché, etc.

cHao
la source