Большинство понимает, что многократное повторение кода (или copy-paste) в примере ниже — зло:
// // здесь и далее - псевдокод // void DrawCircle(int x, int y, int radius) { /// рисуем круг с центром в точке (x, y) радиусом radius } … DrawCircle(getScreenMetrics().width / 2, getScreenMetrics().height / 2, 100500); DrawCircle(getScreenMetrics().width / 2, getScreenMetrics().height / 2, 100600);
Самый очевидный выход из положения — вынести координаты центра круга в отдельные переменные:
ScreenMetrics metrics = getScreenMetrics(); int centerX = metrics.width / 2; int centerY = metrics.height / 2; DrawCircle(centerX, centerY, 100); DrawCircle(centerX, centerY, 200);
В примере выше дублирование устраняется за две секунды потому, что пример высосан из пальца потому, что выражения, задающие координаты центра кругов, полностью совпадают:
getScreenMetrics().width / 2, getScreenMetrics().height / 2
В то же время, далеко не все разработчики умеют распознать и устранить неявное дублирование кода.
Возьмем для примера некое мобильное приложение, которое взаимодействует с API сервера и
позволяет авторизованному пользователю просматривать и загружать фотографии:
HttpResponse login(String email, String password) { HttpRequest request = new HttpRequest(); request.setMethod(“GET”); request.setContentType("application/x-www-form-urlencoded"); String parameters = "?login=" + login + "&password=" + password; String uri = "https://mycoolsite.com/api/login" + parameters; request.setUrl(uri); return request.execute(); } ... HttpResponse getPhotos(int userId) { String uri = "https://mycoolsite.com/api/get_photos?user_id=" + user_id; HttpRequest request = new HttpRequest(); request.setMethod("GET"); request.setUrl(uri); request.setContentType("application/x-www-form-urlencoded"); return request.execute(); } … bool uploadPhoto(Bitmap photo, int user_id) { HttpRequest request = new HttpRequest(); HttpBody body = convertBitmapToHttpBody(photo); request.setUrl("https://mycoolsite.com/api/upload_photo?user_id=" + user_id); request.setMethod("POST"); request.setContentType(“multipart/form-data”); request.setHttpBody(body); HttpResponse response = request.execute(); return (response.getStatusCode()== 200); }
… и еще 100500 подобных методов общим количеством, равным числу вызовов API.
Что произойдет, если mycoolsite.com переедет на awesome.net? Придется искать по всему проекту вызовы request.execute() и править строки. И где-нибудь обязательно забудете подправить URL или просто опечатаетесь. В итоге потратите кучу времени, отправите ближе к ночи “исправленный” билд заказчику, но на следующий день ВНЕЗАПНО получите bug report: “Перестал работать upload фотографий”, например.
Если не писать код сразу “в лоб”, а пойти покурить остановиться и немного подумать, то можно заметить, что все три метода выше делают практически одно и то же:
- создают экземпляр класса HttpRequest
- выставляют необходимые свойства экземпляру (HTTP method, content type)
- задают URL с параметрами
- опционально: добавляют к телу запросы бинарные данные.
Можно без особого труда внести следующие изменения в код:
public class MyRequest extends HttRrequest { private static final String API_BASE_URL = "https://mycoolsite.com/api/"; private Map<String, String> mParameters; private MyRequest() { super(); mParameters = new HashMap<String, String>(); } public static MyRequest createGetRequest() { MyRequest request = new MyRequest(); request.setMethod("GET"); request.setContentType("application/x-www-form-urlencoded"); return request; } public static MyRequest createPostRequest() { MyRequest request = new MyRequest() request.setMethod("POST"); request.setContentType("multipart/form-data"); return request; } public void addParam(String name, String value) { mParameters.put(name, value); } public void addParam(String name, int value) { addParam(name, String.valueOf(value)); } public HttpResponse send(String path) { String uri = API_BASE_URL + path getParametersString(); setUrl(uri); execute(); } private String getParametersString() { StringBuilder result = new StringBuilder("?"); Iterator iterator = mParameters.allKeys().iterator(); while (iterator.hasNext()) { String paramName = iterator.next(); result.add(paramName + "=" + mParameters.get(paramName)); if (iterator.hasNext()) { result.add("&"); } } return result.toString(); } } … HttpResponse login(String login, String password) { MyRequest request = MyRequest.createGetRequest(); request.addParam("login", login); request.addParam("password", password); return request.send("login"); } HttpResponse getPhotos(int userId) { MyRequest request = MyRequest.createGetRequest(); request.addParam("user_id", userId); return request.send("get_photos"); } HttpResponse uploadPhoto(Bitmap photo, int userId) { MyRequest request = MyRequest.createPostRequest(); request.addParam(“user_id”, userId); request.setHttpBody(convertBitmapToHttpBody(photo)); return request.send(“upload_photo”); }
Это был пример нетривиального, многошагового рефакторинга. В итоге появился новый класс MyRequest, являющийся оберткой над системным HttpRequest, с преферансом и куртизанками с возможностью задания параметров запроса и парой других приятных мелочей. Можно также заметить насколько меньше, проще и понятнее стали методы конкретных вызовов API. Добавить новый вызов не составит практически никакого труда.
На такой рефакторинг потратится на пару часов больше времени, чем на добавление еще одного вызова API методом копи-паста. Зато в дальнейшем на внесение изменений и отладку будут тратиться считанные минуты, а не дни.
Естественно, получившийся код далеко не идеален, но всё же он гораздо лучше, чем до рефакторинга, а это уже большой
шаг вперед, причем малой кровью. В дальнейшем, при наличии времени, можно и нужно улучшать этот код.
Мораль: заметили дублирование — устраните его немедленно. Сэкономите в будущем кучу времени и нервов.
При этом, устраняя «сложное» дублирование, потратьте время на повторное проектирование небольшой части проекта,
не пишите код сразу.
Подробнее читайте в книжке Фаулера про рефакторинг.
Буду рад конструктивной критике, предложениям и дополнениям к статье. Спасибо за внимание!
ссылка на оригинал статьи http://habrahabr.ru/post/197932/
Добавить комментарий