冒險村30 - Handle API response with value objects

30 - Handle API response with value objects

本篇將介紹撰寫 Rails 的過程中整個重構的過程,比較偏向一個方向,以一個簡單的例子為例,假設我們請求 API 獲取 Active 的 User 列表:

以下的程式是一個很常見的方法,但這段程式碼中,不只打 Api + parser + 過濾 active user,這樣的方式並沒有辦法讓程式碼只負責一件事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
response = Request.call('some_url')
parsed_response = JSON.parse(response)

eligible_users = []

parsed_response['people'].each do |person|
if person['active'] == true
eligible_users << {
name: "#{person['firstName']} #{person['lastName']}",
email: person['emailAddress']
}
end
end

eligible_users

The parser for the people

Parser 的責任在負責接收並提供方法來 return User

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
response = Request.call('some_url')
parser = PeopleParser.new(response)

eligible_users = []

parser.people.each do |person|
if person['active'] == true
eligible_users << {
name: "#{person['firstName']} #{person['lastName']}",
email: person['emailAddress']
}
end
end

eligible_users

Value object for a person

我們需要一個 pure ruby object 來處理 person

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Person
def initialize(attributes)
@attributes = attributes
end

def active?
@attributes['active']
end

def email
@attributes['emailAddress']
end

def name
"#{@attributes['firstName']} #{@attributes['lastName']}"
end
end

有了上述的 Person Object 我們的 Parser 可以再來進行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class PeopleParser
def initialize(raw_response)
@raw_response = raw_response
end

def people
@parsed_json['people'].map { |attributes| Person.new(attributes) }
end

private

def parsed_json
@parsed_json ||= JSON.parse(@raw_response)
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
response = Request.call('some_url')
parser = PeopleParser.new(response)

eligible_users = []

parser.people.each do |person|
if person.active?
eligible_users << {
name: person.name,
email: person.email
}
end
end

eligible_users

再來繼續重構:

Let person behave like a hash

接下來我們要命名一個 to_h 來組成 name + email 的 hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person
def initialize(attributes)
@attributes = attributes
end

def to_h
{
name: name,
email: email
}
end

def active?
@attributes['active']
end

def email
@attributes['emailAddress']
end

def name
"#{@attributes['firstName']} #{@attributes['lastName']}"
end
end

程式碼也變得更直觀跟可讀性了

1
2
3
4
response = Request.call('some_url')
parser = PeopleParser.new(response)

eligible_users = parser.people.select(&:active?).map(&:to_h)

參考資料