Интро Сейчас актуально писать мелкий говнософт для разных социальных сетей. Обычно начинающие быдлокодеры пишут подобное с помощью процедурного подхода и если проект более-менее сложный начинают захлебываться в глобальных переменных и значениях процедур и функций. Я хочу продемонстрировать ООП подход и его приимущества. В статье будет ценен не код программы (скорее он то только для устрашения), а способ мышления при использовании ООП подхода. Этап 1 ТЗ: Написать дампер сообщений социальной сети ВКонтакте с возможностью дампить переписку как по SID так и по логину/паролю. Оговорюсь заранее, что при написании софта, я встретил много подводных камней, которые значительно усиливали запутанность программы. Я не стал вдаваться в безупречную реализацию, тк, пишу для демонстрации объектно ориентированного метода, но все-же я пометил комментариями участки кода, требующие доработки. Что нам понадобится? 1) Язык поддерживающий ООП (Я пишу на Дэльфи) 2) Сниффер сетевого трафика (работать будем в основном с http поэтому httpAnalyzer вполне устроит). Этап 2 По ООП есть куча разной литературы. Я попробую максимально доступно объяснить всю суть на конкретном примере. Итак, исходя из ТЗ мне нужно создать класс объекта, который смог бы проделывать определенные операции на сайте. Для начала происнифаем сайт и разберемся в алгоритмах запросов. Об этом я вообще писать не буду. По окончании у нас будет алгоритм примерно такой: 1 - Авторизуемся 2 - парсим корешей 3 - парсим месаги Создаем новый класс объекта . Коротко немного теории : каждый класс что-то знает и что-то умеет делать. То что класс знает называется полями класса (если угодно переменные), а то что он умеет - называется методами класса (если угодно процедуры и функции)... В самомо начале проэктируется внешний вид класса (что знает умеет), а потом реализация (как он умеет). Чтож, давайте попробуем определиться, что необходимо знать и уметь нашему классу... Знать: авторизационные данные (логин пароль), SID пользователя, количество друзей пользователя, список ID друзей, и он должен взаимодействовать с сетью по протоколу http, поэтому будет разумно запихнуть в него объект типа TidHTTP. Можно продолжать, но в учебных целях притормозим Уметь: Авторизоваться, Парсить ID друзей, Парсить сообщения. Также, каждый объект класса будет инициализирован под средством метода конструктора Create () . В нем удобно создавать объекту все условия для комфортной работы (создавать файлы/ соединяться с БД/ задавать настройки по умолчанию других объектов.) Code: //---------------Class for work with Vk.com--------- type TVKDump = Class Login, Password : String; // Данные для авторизации Link : String; // Нужная шняга SID : String; // Значение remixsid в кукисах HTTP : TidHTTP; // Для работы с нетом FriendList : TStringList; // Список ИДшников друзей FriendCount : Integer; //Количество друзей function ParseMessage (FriendID : String) : Boolean; //Вытаскиваем сообщения со скритов function Authorize () : Boolean; //Авторизация function ParseFriends () : Integer; //Парсим ИД корешей constructor Create (); // Чето создаем и настраиваем end; //------------------------------------------------- Этап 3 Вот собственно и все! Осталось всего ничего описать реализацию каждой функции . Здесь в подробности я вдаваться не буду, просто выложу свой быдлокод .... Там в основном парсинг и танци с бубном вокруг запросов к ВК. Code: //------------function parse messages with a script-------------------------- function TVKDump.ParseMessage (FriendID : String) : Boolean; var Data, MsgList : TStringList; Name, Mess, tmp : String; i, j : Integer; begin Result := True; try try HTTP.AllowCookies := False; HTTP.Request.CustomHeaders.Clear; HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID); MsgList := TStringList.Create; Data := TStringList.Create; for j := 0 to 5 do begin Data.Clear; Data.Add('act=a_history'); Data.Add('al=1'); // offset - параметр смещения сообщений, можно пробовать подставлять Data.Add('offset=' + IntTostr(j*50)); Data.Add('peer=' + FriendID); Data.Add('whole=0'); tmp := HTTP.Post('http://vk.com/al_im.php', Data); HTTP.AllowCookies := True; Sleep(200); for i := 1 to 100 do begin Delete(tmp, 1, Pos('class="mem_link" target="_blank">', tmp) + 32); Name := Copy(tmp, 1, Pos('</a>', tmp) - 1); Delete(tmp, 1, Pos('</div>', tmp) + 5); Mess := Copy(tmp,1, Pos('</div>', tmp) - 1); MsgList.Add(Name + ':' +Mess); end; end; MsgList.SaveToFile(FriendID + '_damp.txt'); except Result := False; end; finally MsgList.Free; Data.Free; end; end; //---------------------------------------------------------- //-----------------authorization----------------------------- function TVKDump.Authorize () : Boolean; //---------------parse link necessary to request------------- function ParseLink ( Page : String) : Boolean; begin Result := false; Delete(page,1,Pos('action=',page)+15); link := Copy(page,1,pos('>',page)-2); if link <> '' then begin Link := 'http://' + Link; Result := true; end; end; //-------------------------------------------------------------- var Data:TStringList; tmp: String; begin Result := False; try try Data := TStringList.Create; Data.Add('email='+Login); Data.Add('pass='+Password); //----------- реализация быдлятская, но нет времени делать норм ----------------------- ParseLink(HTTP.Get('http://m.vk.com/login')); HTTP.HandleRedirects := False; HTTP.AllowCookies := False; try HTTP.Post(Link, Data); except try HTTP.Get(HTTP.Response.Location); except tmp := HTTP.Response.RawHeaders.Text; Delete(tmp, 1, Pos('remixsid=',tmp) + 8); SID :=Copy( tmp,1 ,Pos('expires=',tmp)-3); end; end; //-------------------------------------------------------------------------------------- HTTP.HandleRedirects := True; HTTP.AllowCookies := True; tmp := HTTP.Get(HTTP.Response.Location); if Pos('logout',tmp) <> 0 then Result := True; except Result := False; end; finally Data.Free; end; end; //-------------parsing function friends ID's ----------------------- // парсит вроде первых 20 чтобы парсило всех нужно допиливать function TVKDump.ParseFriends () : Integer; var tmp: String; i:integer; begin HTTP.AllowCookies := False; HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID); tmp := HTTP.Get('http://m.vk.com/friends'); HTTP.AllowCookies := True; Delete(tmp,1,Pos(' <em>',tmp)+4); FriendCount :=StrToInt(Copy(tmp,1,Pos('</em>',tmp)-1)); for i:=1 to 18 do begin Delete(tmp,1,Pos('<a href="/write',tmp)+14); FriendList.Add(copy(tmp,1,Pos('">',tmp)-1)); end; FriendList.SaveToFile('friends.txt'); end; //----------------------------------------------------------- //-------------- Созаём нужные объекты и настраиваем HTTP ---------------- constructor TVKDump.Create (); begin FriendList := TStringList.Create; HTTP := TIdHTTP.Create(nil); HTTP.HandleRedirects := True; with HTTP.Request do begin UserAgent := 'Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0'; ContentType := 'application/x-www-form-urlencoded'; AcceptLanguage := 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3'; end; end; Этап 4 На 4 этапе необходимо создать объект нашего класса. Для этого его нужно инициализировать методом Create (). Также описывается поведение объекта и взаимодействие с другими компонентами программы. Дабы немного поизвращаться я не стал делать GUI. Программа получает необходимые данные ввиде параметров командной строки, а результаты пишет в файлы. Code: Var VK : TVKDump; i : Integer; begin VK := TVKDump.Create (); //-----------если без запущена параметров, тогда выходим ----------------- if ((Paramstr(1) = '') and (Paramstr(2) = '')) then begin Exit; end; //----------возможность указать SID--------------------------------------- if ((Paramstr(1) = '0') and (Paramstr(2) = '0')) then begin VK.SID := Paramstr(3); VK.ParseFriends(); for i := 1 to 3 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей VK.ParseMessage(VK.FriendList[i]); end else //-----------иначе указываем логин и пароль------------------------ begin VK.Login := Paramstr(1); VK.Password := Paramstr(2); VK.Authorize; VK.ParseFriends(); for i := 1 to 5 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей VK.ParseMessage(VK.FriendList[i]); end; end. Чтобы автоизоваться с помощью логина и пароля нужно в консольке набрать А если по SID: Оутро Моя программа является еще сырым приложением и используется в качестве наглядности, но если есть необходимость и руки = тру, то и интерфейс можно прикрутить и потоки и функции поправить... Подобным образом создаются проекты побольше со сложной структурой. Мир ООП очень богат и при разумном использовании очень помогает упростить жизнь разработчику как професионального уровня так и мелкому фрилансеру. Надеюсь кому-то пригодиться. Спасибо за внимание. BlackIce. Привожу код всего приложения: Code: program VK_Dumper; Uses IdHttp,IdCookieManager,Dialogs, IdHTTPHeaderInfo, Classes, SysUtils; //---------------Class for work with Vk.com--------- type TVKDump = Class Login, Password : String; // Данные для авторизации Link : String; // Нужная шняга SID : String; // Значение remixsid в кукисах HTTP : TidHTTP; // Для работы с нетом FriendList : TStringList; // Список ИДшников друзей FriendCount : Integer; //Количество друзей function ParseMessage (FriendID : String) : Boolean; //Вытаскиваем сообщения со скритов function Authorize () : Boolean; //Авторизация function ParseFriends () : Integer; //Парсим ИД корешей constructor Create (); // Чето создаем и настраиваем end; //------------------------------------------------- //------------function parse messages with a script-------------------------- function TVKDump.ParseMessage (FriendID : String) : Boolean; var Data, MsgList : TStringList; Name, Mess, tmp : String; i, j : Integer; begin Result := True; try try HTTP.AllowCookies := False; HTTP.Request.CustomHeaders.Clear; HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID); MsgList := TStringList.Create; Data := TStringList.Create; for j := 0 to 5 do begin Data.Clear; Data.Add('act=a_history'); Data.Add('al=1'); // offset - параметр смещения сообщений, можно пробовать подставлять Data.Add('offset=' + IntTostr(j*50)); Data.Add('peer=' + FriendID); Data.Add('whole=0'); tmp := HTTP.Post('http://vk.com/al_im.php', Data); HTTP.AllowCookies := True; Sleep(200); for i := 1 to 100 do begin Delete(tmp, 1, Pos('class="mem_link" target="_blank">', tmp) + 32); Name := Copy(tmp, 1, Pos('</a>', tmp) - 1); Delete(tmp, 1, Pos('</div>', tmp) + 5); Mess := Copy(tmp,1, Pos('</div>', tmp) - 1); MsgList.Add(Name + ':' +Mess); end; end; MsgList.SaveToFile(FriendID + '_damp.txt'); except Result := False; end; finally MsgList.Free; Data.Free; end; end; //---------------------------------------------------------- //-----------------authorization----------------------------- function TVKDump.Authorize () : Boolean; //---------------parse link necessary to request------------- function ParseLink ( Page : String) : Boolean; begin Result := false; Delete(page,1,Pos('action=',page)+15); link := Copy(page,1,pos('>',page)-2); if link <> '' then begin Link := 'http://' + Link; Result := true; end; end; //-------------------------------------------------------------- var Data:TStringList; tmp: String; begin Result := False; try try Data := TStringList.Create; Data.Add('email='+Login); Data.Add('pass='+Password); //----------- реализация быдлятская, но нет времени делать норм ----------------------- ParseLink(HTTP.Get('http://m.vk.com/login')); HTTP.HandleRedirects := False; HTTP.AllowCookies := False; try HTTP.Post(Link, Data); except try HTTP.Get(HTTP.Response.Location); except tmp := HTTP.Response.RawHeaders.Text; Delete(tmp, 1, Pos('remixsid=',tmp) + 8); SID :=Copy( tmp,1 ,Pos('expires=',tmp)-3); end; end; //-------------------------------------------------------------------------------------- HTTP.HandleRedirects := True; HTTP.AllowCookies := True; tmp := HTTP.Get(HTTP.Response.Location); if Pos('logout',tmp) <> 0 then Result := True; except Result := False; end; finally Data.Free; end; end; //-------------parsing function friends ID's ----------------------- // парсит вроде первых 20 чтобы парсило всех нужно допиливать function TVKDump.ParseFriends () : Integer; var tmp: String; i:integer; begin HTTP.AllowCookies := False; HTTP.Request.CustomHeaders.Add('Cookie:remixsid=' + SID); tmp := HTTP.Get('http://m.vk.com/friends'); HTTP.AllowCookies := True; Delete(tmp,1,Pos(' <em>',tmp)+4); FriendCount :=StrToInt(Copy(tmp,1,Pos('</em>',tmp)-1)); for i:=1 to 18 do begin Delete(tmp,1,Pos('<a href="/write',tmp)+14); FriendList.Add(copy(tmp,1,Pos('">',tmp)-1)); end; FriendList.SaveToFile('friends.txt'); end; //----------------------------------------------------------- //-------------- Созаём нужные объекты и настраиваем HTTP ---------------- constructor TVKDump.Create (); begin FriendList := TStringList.Create; HTTP := TIdHTTP.Create(nil); HTTP.HandleRedirects := True; with HTTP.Request do begin UserAgent := 'Mozilla/5.0 (Windows NT 5.1; rv:16.0) Gecko/20100101 Firefox/16.0'; ContentType := 'application/x-www-form-urlencoded'; AcceptLanguage := 'ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3'; end; end; Var VK : TVKDump; i : Integer; begin VK := TVKDump.Create (); //-----------если без запущена параметров, тогда выходим ----------------- if ((Paramstr(1) = '') and (Paramstr(2) = '')) then begin Exit; end; //----------возможность указать SID--------------------------------------- if ((Paramstr(1) = '0') and (Paramstr(2) = '0')) then begin VK.SID := Paramstr(3); VK.ParseFriends(); for i := 1 to 3 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей VK.ParseMessage(VK.FriendList[i]); end else //-----------иначе указываем логин и пароль------------------------ begin VK.Login := Paramstr(1); VK.Password := Paramstr(2); VK.Authorize; VK.ParseFriends(); for i := 1 to 5 do // можно ставить VK.FriendsCount но нужно допилить функцию парсинга друзей VK.ParseMessage(VK.FriendList[i]); end; end.
Т.е. берем класс, фигачим в нем всё то же разрозненное нечто, что в теории существовало бы в виде отдельных процедур и пары глобальных переменных и называем это ООП подходом, который обладает "приимуществами" в виде упрощенной поддержки ?
конечно это лишь просто пример работы с классами,и тут можно было бы спокойно обойтись процедурами(мне и самому ближе процедурно-функциональное программирование,ну не люблю я классы ёкрмн,писать по меньшей мере,юзать готовые-да ).Вот банальный пример, когда лучше применить класс:http://mrkto.com/oop_php_class_function/ а так ТС молодец,и идея прикольна
Кстати, давно искал такой софт )) чтоб дампить переписку из акков, и в офф-лайне спокойно читать/разобрать. Можете дать рабочую прогу?
Поправте меня, но на статью и близко не тянет. Что бы представить модель ООП не хватит написать только один класс-обертку. Суть ООП всё таки это взаимодействие между различными обьектами, а не работа с одним единственным классом-обьектом. Как пример обьекта - годится, но не как представление модели ООП.
2 Kaimi, DX вы же делфи не знаете? На статью мало тянет, согласен. Можно было показать примеры наследования и взаимодействия с классами. Но так как сам только практикую, то и писал для себеподобных. Вс-еже, даже такой подход упрощает разработку и разбивает создание на 2 этапа: проэктирование класса, где кодер на абстрактном уровне разрабатывает структуру будущей программы и реализацию. За критику благодарю, буду стараться больше работать в данной сфере и писать более годные статьи. Линк на файл : http://www.sendspace.com/file/dykm4i
В статье хорошо продемонстрирован антипаттерн "God object". Слишком много функционала возложено на один объект. Не обязательно знать делфи, чтобы не заметить такой ошибки. Главная задача ООП - закрывать для изменения существующий и отлаженный код, и предоставить возможность добавлять к нему новую функциональность. За счет этого обеспечивается повторное использование. В данном случае нет смысла в классе TVKDump; с тем же успехом можно было бы сделать все это модулем (delphi unit), а поля - просто переменными этого модуля. Получение страницы с веб-сервера vk.com - в принципе задача для отдельного класса. Ведь можно грузить страницы как напрямую, так и через прокси или цепочки прокси. Если все это запихнуть в один класс, никакой выгоды от ООП не будет, т.к. такой код сложнее выдрать для использования где-либо еще. Страница может загружаться не полностью, а частями (для уменьшения трафика) + списки друзей и прочее могут быть реализованы на сайте постранично (следовательно для каждой страницы требуется отдельный запрос). Аутентификацию тоже следует вынести в отдельный класс, зависящий от соединения с vk (а не просто от сферического TidHTTP, поскольку это внешняя зависимость). Предположим, что вконтакте спалил наш "многопоточный сканер" и решил показать каптчу, на абсолютно произвольном запросе. Теперь придется в код класса внедрять еще и каптчу. Это значит, что надо интегрировать все это дело с antigate и как альтернативу предоставить возможность вбивать их вручную. А если делаем многопоточность, там может возникнуть задача равномерно размыть запросы по всем проксям, с учетом числа запросов и переданного трафика. Тогда потребуется вести учет по трафику и числу запросов каждой прокси - еще один класс, который будет решать, через какой прокси должен быть отправлен запрос в этот раз. Плюс обход всяких сбоев при работе с прокси, необходимость повторения запроса в случае падения коннекта - вообщем, еще много различных проблем можно найти. Особого проектирования в этой статье не усматривается. Обычный код, который пишут на скорую руку. Все, что здесь есть ООПшного - только использование дельфийских классов и компонентов путем композиции. P.S. Для программы уровня "написал и забыл" данный код хорошо годится, главное, чтобы работало. А поддерживать такое не пожелаю никому
То есть ты хочешь сказать, что знаешь Delphi? Или даже ООП? ТС, прочитай про атрибуты видимости методов, переменных, про свойства. Так как ты написал пишут только начинающие быдлокодеры. Ко всему этому ты еще создаешь объекты в конструкторе, но нигде их не освобождаешь. Зачем писать статью с такими ошибками? Чтобы сделать свой членский взнос в литературу для быдлокодеров?