# 模組

## 何謂模組:
1. 任何希望表現得像鴨子類型並且有做角色扮演的物件都可以使用它
2. 可以定義一組被命名的方法,這些方法獨立於類別,並且可以混入到任何物件
## 一個物件可回應的所有訊息包括有:
* 自身實作的訊息
* 所有上游物件所實作的訊息
* 被包含的模組所實作的訊息
* 所有上游物件包含的模組所實作的訊息

```ruby=
class Schedule
attr_accessor :lead_days
def initialize; end
def scheduled?(target, _starting, _ending)
if target.class == Bicycle
lead_days = 1
elsif target.class == Mechanic
lead_days = 4
elsif target.class == Vehicle
lead_days = 3
end
end
def add(target, starting, ending); end
def remove(target, starting, ending); end
end
```

```ruby=
class Bicycle
attr_accessor :lead_days
def initialize
@lead_days = 1
end
end
class Schedule
def initialize; end
def scheduled?(target, _starting, _ending)
target.lead_days
# 可以實作lead_days則為Schedulable
end
def add(target, starting, ending); end
def remove(target, starting, ending); end
end
```
### 以訊息角度思考
```ruby=
bicycle = Bicycle.new
Schedule.new.scheduled?(bicycle, starting, ending)
```
如果你對B物件(bicycle)感興趣,那麼你不應該被迫知道A(Schedule)物件。即使你只是使用它來尋找關於B物件的事情,也不應該這樣做。
**應為**
```ruby=
bicycle.scheduled?(starting, ending)
```
**重構**
```ruby=
class Schedule
def schedule?
false
end
end
class Bicycle
attr_reader :schedule, ...
def initialize(args = {})
@schedule = args[:schedule] || Schedule.new
end
def schedulable?(start_date, end_date)
!scheduled?(start_date-lead_days, end_date)
end
def scheduled?(start_date, end_date)
schedule.scheduled?(self, start_date, end_date)
end
def lead_days
1
end
end
```
### Mechanic和Vehicle也都會扮演這個角色 -> DRY
```ruby=
module Scheulable
attr_writer :schedule
def schedule
@schedule || = ::Schedule.new
end
def schdeuled?(start_date, end_date)
schedule.scheduled?(self, start_date, end_date)
end
def schedulable?(start_date, end_date)
!scheduled?(start_date-lead_days, end_date)
end
def lead_days
0 #默認值
end
end
```


## 結論:
1. 里氏代替原則(Liskov Substitution Principle,LSP )
2. 範本方法模式(Template method pattern)