Статьи

Поведенческие шаблоны проектирования: итератор

Шаблон итератора является одним из наиболее часто используемых шаблонов из поведенческих. В большинстве случаев вы используете его, даже не замечая этого. Предположим, у вас есть контейнер с элементами, и вы хотите пройти их. Итерация элементов может отличаться в зависимости от контейнера, метода извлечения и т. Д. Используя шаблон итератора, мы можем отделить алгоритмы, используемые контейнерами, и методы извлечения и абстрагировать итерацию содержащихся элементов.

Например, когда дело доходит до извлечения выгружаемых данных из конечной точки отдыха, итератор может помочь вам абстрагировать их. Поэтому вы не будете предоставлять пользователю какую-либо информацию о том, как вы получаете следующий пакет данных.

Я сделаю новый интерфейс итератора похожим на тот, который предоставляется языком Java.

1
2
3
4
5
6
7
8
9
package com.gkatzioura.design.behavioural.iterator;
 
public interface Iterator<T> {
 
    boolean hasNext();
     
    T next();
     
}

Мы будем использовать API GitHub jobs, так как он открыт для выдачи запросов отдыха и поиска вакансий. Первая страница — это нулевая страница.

https://jobs.github.com/positions.json?page=0 .

Мы создадим простой объект, который будет содержать идентификатор, название и информацию о компании.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.gkatzioura.design.behavioural.iterator;
 
import java.util.UUID;
 
public class GitHubJob {
 
    private final UUID id;
    private final String company;
    private final String title;
 
    public GitHubJob(UUID id, String company, String title) {
        this.id = id;
        this.company = company;
        this.title = title;
    }
 
    public UUID getId() {
        return id;
    }
 
    public String getCompany() {
        return company;
    }
 
    public String getTitle() {
        return title;
    }
}

Нашим первым шагом будет создание хранилища, которое будет извлекать данные указанной страницы.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.gkatzioura.design.behavioural.iterator;
 
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
 
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
 
public class GitHubJobsRepository {
 
    public static final String GITHUB_JOB_API = "https://jobs.github.com/positions.json?page=";
 
    public List<GitHubJob> fetch(int page) throws Exception {
 
        List<GitHubJob> gitHubJobs = new ArrayList<>();
 
        URL url = new URL(GITHUB_JOB_API+page);
        HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
        String response = IOUtils.toString(httpConnection.getInputStream(), Charset.defaultCharset());
        JSONArray jsonArray = new JSONArray(response);
 
        for(int i=0;i<jsonArray.length();i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            GitHubJob gitHubJob = new GitHubJob(
                    UUID.fromString(jsonObject.getString("id")),
                    jsonObject.getString("company"),
                    jsonObject.getString("title"));
            gitHubJobs.add(gitHubJob);
        }
 
        return gitHubJobs;
    }
 
}

Что хорошо в этом API, так это то, что если вы запрашиваете страницу, которая не существует, вы получаете пустой объект json, таким образом, запрос несуществующей страницы не даст нам исключение, как 404, поэтому нет необходимости обрабатывать ошибки в этом случае. ,

Теперь интересная часть — итератор.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.gkatzioura.design.behavioural.iterator;
 
import java.util.ArrayList;
import java.util.List;
 
public class GitHubIterator implements Iterator<GitHubJob> {
 
    private List<GitHubJob> currentJobsPage = new ArrayList<>();
    private int page = 0;
 
    private final GitHubJobsRepository gitHubJobsRepository;
 
    public GitHubIterator(GitHubJobsRepository gitHubJobsRepository) {
        this.gitHubJobsRepository = gitHubJobsRepository;
    }
 
    @Override
    public boolean hasNext() {
        fetchPageIfNeeded();
        return currentJobsPage.size()>0;
    }
 
    @Override
    public GitHubJob next() {
        fetchPageIfNeeded();
 
        if(currentJobsPage.size()==0) {
            return null;
        }
 
        return currentJobsPage.remove(0);
    }
 
    private void fetchPageIfNeeded() {
        if(page == -1) {
            return;
        }
 
        if(currentJobsPage==null||currentJobsPage.size()==0) {
            try {
                currentJobsPage = gitHubJobsRepository.fetch(page);
                if(currentJobsPage.size()==0) {
                    page = -1;
                } else {
                    page++;
                }
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }
    }
}

Итератор должен содержать запрос страницы от github. Для каждого запрашиваемого элемента один элемент должен быть удален со страницы. Как только страница станет пустой, будет запрошена новая страница.

Независимо от того, запрашивает ли пользователь другой элемент или существует другой элемент, код должен проверить, является ли текущая страница пустой, и если да, он запросит следующую страницу. Если мы получили пустую страницу, это означает, что не должно быть никакого дополнительного запроса к итератору.

Следующим шагом будет добавление метода в репозиторий, который вернет итератор github.

01
02
03
04
05
06
07
08
09
10
public class GitHubJobsRepository {
 
...
 
    public Iterator<GitHubJob> iterator() {
        return new GitHubIterator(this);
    }
 
...
}

Итак, давайте подведем итоги и переберем все записи.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package com.gkatzioura.design.behavioural.iterator;
 
public class IteratorExample {
 
    public static void main(String[] args) throws Exception {
        GitHubJobsRepository gitHubJobsRepository = new GitHubJobsRepository();
 
        Iterator<GitHubJob> gitHubJobIterator = gitHubJobsRepository.iterator();
 
        while (gitHubJobIterator.hasNext()) {
            GitHubJob gitHubJob = gitHubJobIterator.next();
            System.out.println(String.format(" id: %s title: %s company: %s",gitHubJob.getId(),gitHubJob.getTitle(),gitHubJob.getCompany()));
        }
    }
 
}

Вы можете найти исходный код на github .

Также стоит сослаться на шаблоны, которые мы видели ранее, такие как интерпретатор , цепочка ответственности и шаблон команд .

Опубликовано на Java Code Geeks с разрешения Эммануила Гкациоураса, партнера нашей программы JCG. Смотрите оригинальную статью здесь: Поведенческие шаблоны проектирования: итератор

Мнения, высказанные участниками Java Code Geeks, являются их собственными.