JS设计模式---3.封装

封装之利

  • 保证内部数据完整,易于重构
  • 弱化模块间耦合,提高对象可重用性

封装之弊

  • 单元测试困难,错误调试困难
  • 过度封装会损失类的灵活性
  • 对新手不友好

创建对象的基本模式

需求:创建一个存储一本书的类,并为其实现一个以HTML形式显示数据的方法。你只负责创建这个类,别人会创建和使用其实例。

// 使用方式
var theHobbit = new Book('0-395-07122-4','The Hobbit','J.R.R. Tolkien');
theHobbit.display() // 以HTML形式显示数据
门户大开型对象
  var Publication = new interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor',
    'setAuthor', 'display'
  ])
  var Book = function (isbn, title, author) {
    this.setIsbn(isbn);
    this.setTitle(title);
    this.setAuthor(author);
  }
  Book.prototype = {
    checkIsbn = function (isbn) {
      // ...
    },
    getIsbn: function () {
      return this.isbn;
    },
    setIsbn: function (isbn) {
      if (!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN');
      this.isbn = isbn;
    },
    getTitle: function () {
      return this.title;
    },
    setTitle: function (title) {
      this.title = title || 'No title specified';
    },
    getAuthor: function () {
      return this.author;
    },
    setAuthor: function (author) {
      this.author = author || 'No author specified';
    },
    display: function () {
      //  ...
    }
  }

上述代码定义了一个接口,那么其他程序员就应该只能使用接口中定义的属性和方法。
这是门户大开型对象创建方式所能得到的最好结果。一个明确定义的接口,一些数据的赋值器和取值器,及一些检验方法。
缺点就是虽然我们定义了赋值器方法,但是这些属性仍然是公开的、可以直接设置的。

命名规范区分私有成员
 var Book = function (isbn, title, author) {
    this.setIsbn(isbn);
    this.setTitle(title);
    this.setAuthor(author);
  }
  Book.prototype = {
    _checkIsbn = function (isbn) {
      // ...
    },
    getIsbn: function () {
      return this._isbn;
    },
    setIsbn: function (isbn) {
      if (!this.checkIsbn(isbn)) throw new Error('Book: Invalid ISBN');
      this._isbn = isbn;
    },
    getTitle: function () {
      return this._title;
    },
    setTitle: function (title) {
      this._title = title || 'No title specified';
    },
    getAuthor: function () {
      return this._author;
    },
    setAuthor: function (author) {
      this._author = author || 'No author specified';
    },
    display: function () {
      //  ...
    }
  }

这是一种程序员约定俗成的方法,加下划线表示私有变量。但是JS中跟本没有私有变量的定义,只能说是总所周知的命名规范。缺点也很明显,既然是约定,那么只有在遵守时才有效果。

闭包实现私用成员
 var Book = function (newIsbn, newTitle, newAuthor) {
    //私有属性
    var isbn, title, author;
    // 私有方法
    function checkIsbn(isbn) {
      // ...
    };
    // 特权方法
    this.getIsbn = function () {
      return isbn;
    };
    this.setIsbn = function (newIsbn) {
      if (!checkIsbn(newIsbn)) throw new Error('Book:Invalid ISBN');
      isbn = newIsbn;
    };
    this.getTitle = function () {
      return title;
    };
    this.setTitle = function (newTitle) {
      title = newTitle || 'NO title specified';
    };
    this.getAuthor = function () {
      return author;
    };
    this.setAuthor = function (newAuthor) {
      author = newAuthor || 'NO author specified';
    };
    //  构造函数 赋值
    this.setIsbn(newIsbn);
    this.setTitle(newTitle);
    this.setAuthor(newAuthor);
  };
 // 公共方法
  Book.prototype = {
    display() {
      // ...
    }
  }

这种方式创建的对象具有真正的私有属性,解决了可以直接取值赋值的问题。缺点是会耗费更多的内存,而且不利于派生子类。在JS中,用闭包实现私用成员导致的派生问题被称为”继承破坏封装“。

MORE

静态成员

作用域和闭包可用于创建静态成员。大多数方法和属性所关联的是类的实例,而静态成员所关联的是类本身。

  • 一个栗子
  var Book = (function () {
    // 私有静态属性
    var numOfBookes = 0;
    // 私有静态方法
    function checkIsbn(isbn) {
      // ...
    };
    // 返回构造器
    return function (newIsbn, newTitle, newAuthor) {
      // 私有属性
      var isbn, title, author;
      // 特权方法
      this.getIsbn = function () {
        return isbn;
      };
      this.setIsbn = function (newIsbn) {
        if (!checkIsbn(newIsbn)) throw new Error('Book:Invalid ISBN');
        isbn = newIsbn;
      };
      this.getTitle = function () {
        return title;
      };
      this.setTitle = function (newTitle) {
        title = newTitle || 'NO title specified';
      };
      this.getAuthor = function () {
        return author;
      };
      this.setAuthor = function (newAuthor) {
        author = newAuthor || 'NO author specified';
      };

      numOfBookes++;
      if (numOfBookes > 50) {
        throw new Error('Book:Only 50 instance of book can be created.')
      }

      // 赋值
      this.setIsbn(newIsbn);
      this.setTitle(newTitle);
      this.setAuthor(newAuthor);
    }
  })();
  // 公共静态方法
  Book.converToTitleCase = function (inputString) {
    // ...
  };
  // 公共方法 非特权
  Book.prototype = {
    display:function() {
      // ...
    }
  }

闭包,返回一个构造器,私用成员和特权成员仍然在构造器中。但是闭包中可以访问静态成员。优点在于所有的静态成员只会存在一份,这样大大减小了内存的消耗。
q:如何判断是否设计成静态成员?
a:一般情况下,我们只需要看一个属性或者方法是否需要访问实例数据。如果不需要,那么设计成静态成员会更有效率。

常量

常量的取值

  var Class = (function () {
    var constans = {
      UPPER_BOUND: 100,
      LOWER_BOUND: -100
    };
    var ctor = function () {
      // ...
    }
    // 特权静态方法
    ctor.getConstans = function (name) {
      return constans[name];
    }
    return ctor
  })()
  // 取值
  Class.getConstans('UPPER_BOUND')

你可能感兴趣的:(JS设计模式---3.封装)