---
# System prepended metadata

title: Head First OOA&D - Ch5(1)
tags: [HeadFirstOOAD, ' AbstractClass']

---

---
tags: HeadFirstOOAD, AbstractClass
---

# Head First OOA&D - Ch5(1)
- 感謝[Coder Clark](https://medium.com/@CoderClark)大神指導，參考大神文章讓code寫更好！
- [完整實作](https://github.com/bobolee1239/HeadFirstOoadCpp/tree/main/RickShop)

## GOAL 
客戶除了賣吉他之外也想賣Mandolin，希望我們可以擴充App

## Key Idea 
> 1. Abstract classes are placeholders for actual implementation classes.
> 2. Abstract class defines "behavior", and the subclasses "implement" that behavior.

## Design
考慮每樣樂器都會有自己的spec且有相同的價錢與serial number欄位，為了方便擴充與增加的code reusibility，決定拉出兩個abstract class: `Instrument`, `InstrumentSpec`存放所有樂器（spec）共同有的特性，新的樂器都需要繼承這樣兩個class並實作對應的compare method，倉庫（`Inventory`）只存放parent class的參考，尋找時用多型來找合適的spec。

**Origin Class Diagram**
```mermaid 
classDiagram

Inventory --> "*" Guitar : stock
Guitar o-- "1" GuitarSpec : spec

class GuitarSpec {
    Builder build
    Type type
    Wood topWood
    Wood backWood
    String model
    Int numString
    Builder getBuilder()
    String getModel()
    Type getType()
    Wood getBackWood()
    Wood getTopWood()
    Int getNumStrings()
    bool equal(GuitarSpec)
}

class Guitar {
    String serialNumber
    Double price
    GuitarSpec getSpec()
    String getSerialNumber()
    Double getPrice()
    setPrice(Double)
}

class Inventory {
    addGuitar(String, Double, GuitarSpec)
    Guitar get(String)
    Guitar[*] search(GuitarSpec)
}
```

**New Class Diagram**
```mermaid 
classDiagram

Inventory --> "*" Instrument : stock
Instrument o-- "1" InstrumentSpec : spec

GuitarSpec --|> InstrumentSpec 
MandolinSpec --|> InstrumentSpec
Guitar --|> Instrument
Mandolin --|> Instrument

class InstrumentSpec {
<<abstract>>
    Builder build
    Type type
    Wood topWood
    Wood backWood
    String model
    Builder getBuilder()
    String getModel()
    Type getType()
    Wood getBackWood()
    Wood getTopWood()
    virtual bool equal(InstrumentSpec)
}

class Instrument {
<<abstract>>
    String serialNumber
    Double price
    InstrumentSpec getSpec()
    String getSerialNumber()
    Double getPrice()
    setPrice(Double)
}

class GuitarSpec {
    Int numString
    Int getNumString
    bool equal(InstrumentSpec)
}

class MandolinSpec {
    Style style
    Style getStyle()
    bool equal(InstrumentSpec)
}

class Inventory {
    addInstrument(String, Double, InstrumentSpec)
    Instrument get(String)
    Guitar[*] search(GuitarSpec)
    Mandonlin[*] search(MandolinSpec)
}
```

## Cpp Implementation (First Version)

由於 `InstrumentSpec` 是 abstract class 因此 `Instrument` 只能存放其參考，並於derived class存放spec實體，在construct時再將spec參考傳給base class。

```c++
class Instrument {
public:
    Instrument(
        const std::string    &serialNumber, 
        const double          price, 
        InstrumentSpec       &spec
    ) : serialNumber(serialNumber), price(price), spec(spec)
    {}
    
    const InstrumentSpec& getSpec() const
    {return spec;}
    
private:
    std::string     serialNumber;
    double          price;
    InstrumentSpec &spec;
};

class Guitar : public Instrument
{
public:
    Guitar(
        const std::string &serialNumber, 
        const double       price,
        GuitarSpec        &spec
    ) : Instrument(serialNumber, price, static_cast<InstrumentSpec&>(this->spec)), spec(spec)
    {}
private:
    GuitarSpec spec;
};
```

Usage
```c++
class Inventory 
{
    // using example (add)
    int addInstrument(
        const std::string    &serialNumber,
        const double          price,
        InstrumentSpec      &&spec
    );
     
    std::list<const Instrument*> search(const InstrumentSpec& target)
    {
        std::list<const Instrument*> matches;
        for (auto& pItem : guitarStock)
        {
            // using example (search)
            if (pItem->getSpec() == target)
            {
                matches.push_back(static_cast<const Instrument*>(pItem));
            }
        }
        return matches;
    }
};

// using example (add)
inventory.addInstrument("11277", 3999.95, 
    GuitarSpec(COLLINGS, "CJ", ACOUSTIC, 
        6, INDIAN_ROSEWOOD, SITKA
        ));
            
```

## Cpp Implementation (Update Version)

### (1) Reference to Abstract Class
先前parent class存放參考，卻沒有在code裡面明確地限制只能參考derived class的實體，會有幾個明顯缺點：
1. Derived class 有機會不按照預設邏輯，傳入其他spec的參考。
2. 其他人extend `Instrument`時，必須要參考比如`Guitar`實作才會知道應該要傳入derived class 的spec實體參考。

**目標：** 參考Coder Clark大神精準法第三招[畫地自限](https://medium.com/@CoderClark/b0003-%E6%98%93%E8%AE%80%E7%A8%8B%E5%BC%8F%E7%A2%BC%E7%9A%84%E7%89%B9%E6%80%A7-%E7%B2%BE%E6%BA%96-135ec08a8961)，限制`Instrument`只能reference到子類別的spec。

#### The First Idea
第一個改進的想法是如果讓`InstrumentSpec`不是abstract class這樣`Instrument`就可以存放`InstrumentSpec`實體，construct時再將實體傳入copy一份，但這樣會衍生其他問題：
1. Derived class 也會存放其對應的spec, e.g. `GuitarSpec`。`GuitarSpec` 又是繼承 `InstrumentSpec` 等於存放了兩個實體。 

> **[Note]** 
> 如果`Spec`體系不是繼承，而是`InstruemntSpec`存放common資料，`GuitarSpec`存放額外的資料，或許可以考慮這樣做。

#### The Second One
第二個想法是因為最終parent class還是只能有參考而實體必須存在derived class內，所以如果跳脫`Instrument`必須要包含`InstrumentSpec`的框架，就能考慮把回傳參考的實作職責下放給derived class，而base class只需要訂好介面並強迫子類別實作（pure virtual method)，也呼應到Key Idea的第二點！

> **心得：** 
> 如果不適合在 abstract class 中創建placeholder，不如換個想法讓abstract class定義behavior，把實作權責下放給derived classes。

```c++
class Instrument {
public:
    Instrument(
        const std::string    &serialNumber, 
        const double          price
    ) : serialNumber(serialNumber), price(price)
    {}
    
    virtual const InstrumentSpec& getSpec() const = 0;
    
private:
    std::string     serialNumber;
    double          price;
};

class Guitar : public Instrument
{
public:
    Guitar(
        const std::string &serialNumber, 
        const double       price,
        GuitarSpec        &spec
    ) : Instrument(serialNumber, price), spec(spec)
    {}
    
    const InstrumentSpec& getSpec() const
    {return static_cast<InstrumentSpec&>spec;}
    
private:
    GuitarSpec spec;
};
```

// TODO: visiter to improve polymorphism of compare func

