Форум программистов, компьютерный форум CyberForum.ru

О написании чистого кода - Objective-C

Восстановить пароль Регистрация
 
residentkms
21 / 21 / 8
Регистрация: 20.10.2013
Сообщений: 138
Завершенные тесты: 1
11.11.2015, 17:43     О написании чистого кода #1
Вообщем, такая проблема.
Мне не нравится подобные нагромождения из if-ов, добавления и удаления оверлеев, бесконечных диалоговых окон:
Objective-C
1
2
3
4
5
6
7
8
9
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
53
54
55
56
57
58
59
60
61
62
UIView *overlay = [[UIView alloc]initWithFrame:self.view.frame];
    overlay.backgroundColor = [UIColor colorWithWhite:0.5f alpha:0.5f];
    overlay.userInteractionEnabled = YES;
    [self.view addSubview:overlay];
    
    UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
    activity.color = [UIColor blackColor];
    activity.center = self.view.center;
    [self.view addSubview:activity];
    [activity startAnimating];
    
    NSString *parameters = [@"" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@""]];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    request.HTTPMethod = @"post";
    request.HTTPBody = [parameters dataUsingEncoding:NSUTF8StringEncoding];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response,
                                                                                                            NSData * _Nullable data,
                                                                                                            NSError * _Nullable connectionError) {
        [overlay removeFromSuperview];
        [activity removeFromSuperview];
        
        if (!connectionError) {
            if ([(NSHTTPURLResponse *)response statusCode] == HTTP_OK) {
                @try {
                    NSError *error;
                    _info = [NSJSONSerialization JSONObjectWithData:data
                                                            options:NSJSONReadingMutableContainers
                                                              error:&error];
                    if (error) {
                        @throw [NSException exceptionWithName:@"Parse error"
                                                       reason:@"Unknown"
                                                     userInfo:nil];
                    }
                    
                    ////
                }
                @catch (NSException *exception) {
                    _info = nil;
                    [[[UIAlertDialog alloc]initWithStyle:UIAlertDialogStyleAlert
                                                   title:@"Ошибка"
                                                 message:@"Произошла неизвестная ошибка"
                                              closeTitle:@"OK"] showInViewController:self];
                }
            } else if ([(NSHTTPURLResponse *)response statusCode] == HTTP_BAD_REQUEST) {
                [[[UIAlertDialog alloc]initWithStyle:UIAlertDialogStyleAlert
                                               title:@"Ошибка"
                                             message:@"Проверьте правильность ввода"
                                          closeTitle:@"OK"] showInViewController:self];
            } else {
                [[[UIAlertDialog alloc]initWithStyle:UIAlertDialogStyleAlert
                                               title:@"Ошибка"
                                             message:@"Произошла неизвестная ошибка"
                                          closeTitle:@"OK"] showInViewController:self];
            }
        } else {
            [[[UIAlertDialog alloc]initWithStyle:UIAlertDialogStyleAlert
                                           title:@"Ошибка"
                                         message:@"Ошибка соединения"
                                      closeTitle:@"OK"] showInViewController:self];
        }
    }];
Такое повсюду, и это не самый тяжелый случай.
Как в таких случаях рефакторить? Инкапсулировать в какой то класс (что то типа MyHTTPClient) а в нем все в отдельные методы под каждый реквест? Или как то еще мб?
Или может быть есть где то красиво выглядящие сорцы, на которые можно поглазеть и чему то научиться?
Вообщем, очень нужна помощь
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
LeninRedStar
5 / 5 / 4
Регистрация: 22.11.2015
Сообщений: 21
22.11.2015, 22:11     О написании чистого кода #2
В коде видно что часто смешивается логика и интерфейс, т.е. могу подозревать, что этот код повторяется во многих местах, что в дальнейшем может привести к проблемам, поскольку противоречит MVC. Логику общения с сервером обычно выносят в один или несколько менеджеров. Обработка ошибок соединения прячется в эти менеджеры. Интерфейс такого менеджера может выглядеть как то так:

-(void) doSomeThingWithServerSuccess:^(NSArray *data)
Error:^(NSError *error);

В Блоке ошибки можно показывать соответствующее сообщение или передать ошибку дальше по стеку вызовов, в саксесс выполнять работу с полученными данными.

Прошу прощения за примерный синтаксис блоков, на память с ними мне сложно ))
residentkms
21 / 21 / 8
Регистрация: 20.10.2013
Сообщений: 138
Завершенные тесты: 1
23.11.2015, 11:04  [ТС]     О написании чистого кода #3
Ну вот я в итоге и обернул все это, оставив обработку стандартных ошибок (ошибка соединения/невалидные данные) внутри. Количество кода, конечно, уменьшилось в разы, но я до сих пор не уверен в подобном подходе.

Objective-C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@interface W4UHTTPClient : NSObject
 
typedef enum W4UHTTPMethod{
    W4UHTTPMethodPost,
    W4UHTTPMethodGet
}W4UHTTPMethod;
 
@property (assign, nonatomic) BOOL showErrorDialogs;
 
@property (assign, nonatomic) BOOL showOverlay;
 
@property (copy, nonatomic) NSString *baseUrl;
 
- (void)asyncRequestWithPath:(NSString *)url
                      method:(W4UHTTPMethod)method
                  parameters:(NSDictionary *)parameters
                    callback:(void(^)(NSHTTPURLResponse *response, NSData *data, W4UError *error))callback;
 
- (void)asyncJsonRequestWithPath:(NSString *)url
                          method:(W4UHTTPMethod)method
                      parameters:(NSDictionary *)parameters
                        callback:(void(^)(NSHTTPURLResponse *response, id json, W4UError *error))callback;
LeninRedStar
5 / 5 / 4
Регистрация: 22.11.2015
Сообщений: 21
23.11.2015, 11:28     О написании чистого кода #4
Сообщение было отмечено автором темы, экспертом или модератором как ответ
Подход нормальный, просто нужно понимать что этого не решение всех проблем. Я так понял есть некий класс сейчас, который может выполнить запрос. В чистом виде его использовать из вьюконтроллеров очевидно не стоит. Над ним должны стоять более ориентированые на логику приложения классы, которые будут уже понимать что они получают либо посылают на сервер, сами умеют создавать наборы параметров для asyncRequest и распарсить приходящий результат.

Вью контроллер по хорошему должен иметь зависимость от некого протокола (класса, удовлетворяющего протоколу), который имеет методы, нужные именно этому контроллеру. Пример: есть логин скрин. У него должен быть член класса некий id<LoginProtocol> loginer. Этот loginer ему устанавливается извне, таким образом вьюконтроллер не знает даже какого он класса и ты всегда сможешь легко подменить один класс другим если вдруг поменялось взаимодействие с сервером. Сам протокол например имеет методы:

-(void) loginWithUser: (User * ) user
success:^(void) successBlock
error:^(NSError *error) errorBlock;

-(void) logout...


При этом реальный класс менеджер, который сетапят контроллеру может удовлетворять сразу нескольким протоколам, но т.к. про остальные протоколы логин скрин не знает, он других методов вызвать не может.

Далее схема может усложняться если в приложении например нужен кэш или некий оффлайн мод. Тогда уже можно вводить классы репозитории, которые в зависимости от того есть сеть или нет, будут обращаться либо к серверу либо к БД, при этом опять же скринам будет все равно куда они обратились, они свои данные в любом случае получат.
residentkms
21 / 21 / 8
Регистрация: 20.10.2013
Сообщений: 138
Завершенные тесты: 1
23.11.2015, 11:41  [ТС]     О написании чистого кода #5
Цитата Сообщение от LeninRedStar Посмотреть сообщение
Вью контроллер по хорошему должен иметь зависимость от некого протокола (класса, удовлетворяющего протоколу), который имеет методы, нужные именно этому контроллеру
То есть, реализовывать логику (login) нужно возлагать на протокол, а не на сущность (того же юзера, например)?
LeninRedStar
5 / 5 / 4
Регистрация: 22.11.2015
Сообщений: 21
23.11.2015, 12:04     О написании чистого кода #6
конечно. Юзер должен быть контейнером, который просто хранит данные, т.е. моделью. Вообще модели не должны сами обращатсья к серверу. Объясню на примере юзера. Предположим что мы сделали логин прямо в классе User. При логине мы получаем от сервера некий токен или сессию, которая будет нужна другим запросам. В этом случае каждой другой модели, которая обращается к серверу чтобы что-то в себе обновить, понадобится эта сессия. И тут либо нужно во всех них иметь ссылку на юзера либо сделать чтобы юзер еще и сессию куда нибудь сохранил, а все остальные знали бы куда он ее сохранил. Код сразу становится путанным, и чем больше и сложнее модель, тем более путанным становится код.
residentkms
21 / 21 / 8
Регистрация: 20.10.2013
Сообщений: 138
Завершенные тесты: 1
23.11.2015, 12:21  [ТС]     О написании чистого кода #7
Спасибо за столь развернутые ответы, одно лишь осталось не совсем понятно.

Цитата Сообщение от LeninRedStar Посмотреть сообщение
При этом реальный класс менеджер, который сетапят контроллеру может удовлетворять сразу нескольким протоколам, но т.к. про остальные протоколы логин скрин не знает, он других методов вызвать не может.
Как устанавливается менеджер конкретному скрину?
LeninRedStar
5 / 5 / 4
Регистрация: 22.11.2015
Сообщений: 21
23.11.2015, 12:56     О написании чистого кода #8
Сообщение было отмечено автором темы, экспертом или модератором как ответ
через фабрику либо прямо в сторике через dataObject. Однако тут нужно иметь в виду, что через сторик у тебя будет отдельный экземпляр, в случае если ты юзаешь синглтон, могут возникнуть сложности.

Фабрика предпочтительнее
residentkms
21 / 21 / 8
Регистрация: 20.10.2013
Сообщений: 138
Завершенные тесты: 1
23.11.2015, 13:36  [ТС]     О написании чистого кода #9
dataobject?
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
23.11.2015, 14:40     О написании чистого кода
Еще ссылки по теме:

Objective-C Что значат собака (@) и процент (%) в тексте кода
Objective-C Добавление кода отправки GET запроса в AppDelegate.m
Objective-C Возникла сложность в реализации кода. Начинающий в Objective C
Ошибки в написании кода PHP

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
LeninRedStar
5 / 5 / 4
Регистрация: 22.11.2015
Сообщений: 21
23.11.2015, 14:40     О написании чистого кода #10
ты можешь в сторике добавить на сцену Object из компонентов. Это может быть любой класс
Yandex
Объявления
23.11.2015, 14:40     О написании чистого кода
Ответ Создать тему
Опции темы

Текущее время: 19:31. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru