1
votes

Fonctions JS et classe abstraite et prototypes

Voici une version simplifiée de mon code:

  function TextBox () {
    this.builddom = function () {
      // Building the text dom
    }
  }
  function ImageBox () {
    this.builddom = function () {
      // Building the image dom
    }
  }
  function Box (type) {
    var handler =
      (type ==  'text') TextBox  :
      (type == 'Image') ImageBox : null;
    if (handler) (handler).call (this);

    this.builddom = function () {
      // Here I would like to call the correct builddom function for the type.
    }
  }
  var textbox = new Box ('text');
  textbox.builddom ();

Si Box.builddom n'existe pas, cela fonctionne bien, la fonction builddom associée au type spécifique est appelée. Mais je dois faire quelque chose de général dans Box, puis appeler le builddom spécifique. Si je donne un nom différent à Box builddom, disons Box.dobuilddom, c'est bien aussi, mais empêche l'accès générique aux Boxes.

Je pense qu'une manipulation de prototype intelligente peut faire le travail, mais je n'ai pas pu trouvez-le.


3 commentaires

pourquoi n'étendez-vous pas Box des 2 autres fonctions?


C'est une structure très étrange. Vous semblez vouloir faire en sorte que new Box renvoie des instances qui héritent de TextBox ou ImageBox en fonction du paramètre de Box . C'est possible , mais franchement pas une bonne idée.


@ T.J.Crowder Je suis entièrement d'accord. Cela n'a aucun sens du point de vue de l'abstraction. En POO, vous utiliseriez la délégation et en passant le handler directement au lieu de passer le type et d'essayer de déterminer quel handler utiliser en fonction de cela . Sinon, vous auriez une sorte d'usine qui vous renvoie différentes boîtes. Il y a très peu de raisons d'avoir Box () tel quel actuellement.


6 Réponses :


2
votes

Il vaudrait peut-être mieux éviter le prototypage et utiliser la composition à la place:

function TextBox(box) {
  this.builddom = function() {    
    console.log('Building the text dom', box.props);
  }
}

function ImageBox(box) {
  this.builddom = function() {
    console.log('Building the image dom', box.props);
  }
}

function Box(props) {
  this.props = props;
  this.builddom = function() {
    throw new Error('unsupported function');
  }
}

var textbox = new TextBox(new Box({size:5}));
textbox.builddom();


0 commentaires

0
votes

Si vous souhaitez utiliser le prototypage, vous pouvez le faire comme ceci:

function TextBox(props) {
    this.props = props;
}

TextBox.prototype = {
    builddom: function () {
      // Building the text dom
      console.log("TextBox", this.props);
    }
}

function ImageBox(props) {
    this.props = props;
}

ImageBox.prototype = {
    builddom: function () {
      // Building the text dom
      console.log("ImageBox", this.props);
    }
}

function Box (type, props) {
  var handler = (type ==  'text') ? TextBox :
    (type == 'Image')  ? ImageBox : null;
  if (handler) {
     handler.call(this, props);
     Object.assign(this, handler.prototype);     
  }
}

var textbox = new Box ('text', {text: 'some'});
textbox.builddom ();

var imagebox = new Box ('Image', {x: 1, y: 2});
imagebox.builddom ();


2 commentaires

Wow, juste 1 heure et de nombreuses réponses. Je vais clarifier un peu. En fait, TextBox et ImageBox ne sont pas censés être écrits par moi. La fonction Box fait partie d'une boîte à outils, les autres développeurs devraient créer des xxxBoxes avec la contrainte d'exposer une méthode builddom (et bien d'autres). La boîte à outils propose une sorte d'enregistrement de Box pour pouvoir utiliser ce nouveau type de Box. C'est le cas parfait pour l'utilisation d'une «interface» ou d'une «classe abstraite» en Java. Je ferais mieux d'utiliser la fonction «classe» JS, mais elle n'est pas encore utilisable dans tous les navigateurs.


Désolé pour le commentaire interrompu, je ne savais pas que le fait d'appuyer sur la touche Entrée l'envoyait immédiatement. Alors ... Tout mon code se réfère uniquement à la fonction Box. Les autres xxxBox peuvent s'y référer également. Par exemple, les TextBoxes peuvent utiliser des ImageBoxes. C'est la raison de proposer une interface unique à toutes les box. J'espère que c'est plus clair. Claude.



1
votes

Je ne comprends pas vraiment le concept. La boîte n'est qu'une sorte de conteneur. Il ne fait rien mais crée une nouvelle instance. Ce dont vous auriez vraiment besoin ici, c'est d'une interface Box, mais js n'a pas d'interfaces. Vous pouvez utiliser TypeScript si vous voulez ...

  function Box (){}

  function TextBox () {}
  TextBox.prototype = Object.create(Box.prototype, {
    constructor:TextBox,
    builddom: function () {
      // Building the text dom
    }
  });

  function ImageBox () {}
  ImageBox.prototype = Object.create(Box.prototype, {
    constructor:ImageBox,
    builddom: function () {
      // Building the image dom
    }
  });

  var container = {
    createBox: function (type){
        if (type == "text")
            return new TextBox();
        else if (type == "image")
            return new ImageBox();
        else
            throw new Error();
    }
  };

  var textbox = container.createBox('text');
  console.log(
    textbox instanceof Box, 
    textbox instanceof ImageBox, 
    textbox instanceof TextBox
  );
  textbox.builddom();

Une autre option consiste à utiliser un proxy si vous voulez envelopper des objets, mais je ne pense pas que ce soit votre objectif ici.

Si vous avez besoin d'une vérification de type plus tard, vous pouvez utiliser l'héritage, mais il n'y a pas d'héritage multiple, donc même de cette façon, vous ne pouvez pas imiter les interfaces. Cela va de cette façon btw.

  function TextBox () {
    this.builddom = function () {
      // Building the text dom
    }
  }
  function ImageBox () {
    this.builddom = function () {
      // Building the image dom
    }
  }

  var container = {
    createBox: function (type){
        if (type == "text")
            return new TextBox();
        else if (type == "image")
            return new ImageBox();
        else
            throw new Error();
    }
  };

  var textbox = container.createBox('text');
  textbox.builddom();


0 commentaires

0
votes

Il n'est pas nécessaire de créer une classe box si vous ne comptez pas l'utiliser, créez plutôt une fonction de fabrique et retournez une nouvelle instance de la classe respective.

function AbstractBox() {}
AbstractBox.prototype.builddom = function() {
  console.warn("unimplemented method");
};

function TextBox() {}
TextBox.prototype.builddom = function() {
  console.log("TextBox.builddom called");
};

function ImageBox() {}
ImageBox.prototype.builddom = function() {
  console.log("ImageBox.builddom called");
};

function ErrorBox() {}



function createBox(type) {
  var handler = Object.create(({
    "text": TextBox,
    "Image": ImageBox
  }[type] || ErrorBox).prototype);
  handler.constructor.apply(handler, [].slice.call(arguments, 1));
  for (var property in AbstractBox.prototype) {
    var method = AbstractBox.prototype[property];
    if (typeof method === "function" && !(property in handler)) handler[property] = method;
  }
  return handler;
}

(createBox("text")).builddom(); // Text
(createBox("Image")).builddom(); // Image
(createBox("error")).builddom(); // Error


1 commentaires

J'utilise la classe Box, en fait, c'est la seule classe que j'utilise pour accéder aux Box. TextBox et ImageBox sont juste là pour que la classe Box les encapsule. J'ai une solution, c'est d'utiliser des noms différents pour builddom, disons builddom dans Box et dombuild il toute l'implémentation de xxxBox, ce n'est pas très propre et devrait être évitable.



0
votes

Il n'est pas clair pourquoi vous n'utilisez pas uniquement l'héritage de prototype standard ici. Cela vous permettra à la fois d'hériter ou de remplacer les méthodes du parent. Par exemple, ImageBox hérite de la méthode parente et TextBox remplace:

/* Define Box */
function Box (type) {
    this.type = type || 'box'
}
Box.prototype.builddom = function (){
    console.log(this.type, ": build called")
}

/* Define TextBox */
function TextBox () {
    Box.call(this, "text")
}
TextBox.prototype = Object.create(Box.prototype);

/* Override method */
TextBox.prototype.builddom = function (){
    // call parent method too?
    // Box.prototype.builddom.call(this)
    console.log(this.type, "Text box override method")
}

/* Define ImageBox */
function ImageBox () {
    Box.call(this, "image")
}
ImageBox.prototype = Object.create(Box.prototype);


var box = new Box ();
box.builddom();

var textbox = new TextBox ();
textbox.builddom();

var imageBox = new ImageBox ();
imageBox.builddom();


0 commentaires

0
votes

Ma suggestion est d'utiliser la composition / délégation plutôt que l'héritage (has-a au lieu de is-a).

function TextBox () {
    this.builddom = function () {
      // Building the text dom
    }
  }
  function ImageBox () {
    this.builddom = function () {
      // Building the image dom
    }
  }
  function Box (type) {
    var constructor =
      (type ==  'text') ? TextBox  :
      (type == 'Image') ? ImageBox : null;
    var delegate = new constructor();

    this.builddom = function () {
      // Pre-work goes here.
      delegate.builddom();
      // Post-work goes here.
    }
  }
  var textbox = new Box ('text');
  textbox.builddom ();


0 commentaires