1. 程式人生 > 其它 >c++設計模式之抽象工廠

c++設計模式之抽象工廠

棧是一種遵從後進先出LIFO(Last In First Out)原則的有序集合。新新增或待刪除的元素都儲存在棧的同一端,稱作棧頂,另一端就叫棧底。在棧裡,新元素都靠近棧頂,舊元素都接近棧底。

生活中的案例

  • 堆成山的書
  • 堆成山的碗具

這些案例中所有的特點都是新增的時候放在最頂端,移除的時候都是從頂端開始移除。

建立一個 Stack

接下來 我們用 JavaScript 這門語言來描述 這種資料解構。

class Stack {
  constructor() {
    this.items = {};
  }
}

我們使用了 JavaScript 的 物件 來 儲存棧結構,接下來我們需要遵循(LIFO)原則,對元素的新增和刪除做一些限制。

Stack 擁有的方法?

  • push(element):新增一個 或 幾個新元素 到棧頂。
  • pop():移除棧頂的元素,並返回移除的元素。
  • peek(): 返回棧頂的元素,不做任何改變。
  • isEmpty():如果棧裡沒有任何元素就返回 true,反之返回false
  • clear():移除棧裡所有的元素。
  • size(): 返回棧裡的元素個數,和 陣列的length屬性相似。
  • toString(): 返回當前棧所有元素的內容。
class Stack {
  constructor() {
    this.items = {}; // 儲存元素
    this.count = 0; // 記錄棧元素的個數
  }
  // methods
  push(element) {
    this.items[this.count] = element;
    this.count++;
  }
  pop() {
    if (this.isEmpty()) {
      return undefined;
    }
    this.count--;
    const current = this.items[this.count];
    delete this.items[this.count];
    return current;
  }
  peek() {
    if (this.isEmpty()) {
      return undefined;
    }
    return this.items[this.count - 1];
  }

  clear() {
    this.items = {};
    this.count = 0;
    // or
    // while (!this.isEmpty()) {
    //   this.pop()
    // }
  }

  isEmpty() {
    return this.count === 0;
  }

  toString() {
    if (this.isEmpty()) {
      return "";
    }
    let str = `${this.items[0]}`;
    let index = 1;
    while (index < this.count) {
      str = `${str},${this.items[index]}`;
      index++;
    }
    return str;
  }
}

操作

const stack = new Stack();

stack.push(1); // 1
stack.push("a"); // 1,a
stack.peek(); // a
stack.pop(); // a
stack.toString(); // 1

問題

在 JavaScript 裡,這個stack例項可以直接訪問到items,count

const stack = new Stack();
stack.items = { 1: "a", 5: "c" };

我們希望保護內部的元素,只有我們暴露出的方法才能修改內部結構。不幸的是,我們在 Stack 類中宣告的 items 和 count 屬
性並沒有得到保護,因為 JavaScript 的類就是這樣工作的。

下劃線命名約定

一部分開發者喜歡在 JavaScript 中使用下劃線命名約定來標記一個屬性為私有屬性。

class Stack {
  constructor() {
    this._items = {};
    this._count = 0;
  }
}

用 ES2015 的限定作用域 Symbol 實現類

const _items = Symbol("stackItems");
class Stack {
  constructor() {
    this[_items] = {};
  }
}
  • 不過依然可以訪問到這個屬性
const _items = Symbol("stackItems");
class Stack {
  constructor() {
    this[_items] = {};
  }
}
const stack = new Stack();
const symbolKeys = Object.getOwnPropertySymbols(stack); // [Symbol(stackItems)]
stack[symbolKeys[0]] = { 1: "a", 4: "5" };
stack.toString(); // a,5

使用棧來解決的問題

  • JavaScript 執行棧

5 分鐘理解 JS 呼叫棧

深入理解 JavaScript 中的執行堆疊

  • 十進位制轉換
function baseConverter(decNumber, base) {
  const remStack = new Stack();
  const digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  let number = decNumber;
  let rem;
  let baseString = "";
  if (!(base >= 2 && base <= 36)) {
    return "";
  }
  while (number > 0) {
    rem = Math.floor(number % base);
    remStack.push(rem);
    number = Math.floor(number / base);
  }
  while (!remStack.isEmpty()) {
    baseString += digits[remStack.pop()];
  }
  return baseString;
}

baseConverter(100, 8); // '144'
baseConverter(50, 2); // '110010'