{"id":37,"date":"2009-07-17T00:05:49","date_gmt":"2009-07-16T23:05:49","guid":{"rendered":"http:\/\/bielamowicz.info\/blog\/index.php\/2009\/w-obronie-kohezji-logicznej\/"},"modified":"2018-07-04T21:00:02","modified_gmt":"2018-07-04T20:00:02","slug":"kohezja-logiczna","status":"publish","type":"post","link":"http:\/\/bielamowicz.info\/index.php\/2009\/kohezja-logiczna\/","title":{"rendered":"W obronie kohezji logicznej"},"content":{"rendered":"<p>W swej znakomitej ksi\u0105\u017cce <a href=\"http:\/\/www.cc2e.com\/\" target=\"_blank\" rel=\"noopener\">\u201eCode Complete\u201d<\/a> Steven McConnell rozwa\u017ca mi\u0119dzy innymi poj\u0119cie <a href=\"http:\/\/en.wikipedia.org\/wiki\/Cohesion_(computer_science)\">kohezji<\/a>.\u00a0 W du\u017cym skr\u00f3cie \u2013 kohezja jest miar\u0105 \u201espoisto\u015bci\u201d danego modu\u0142u, klasy lub funkcji. Im wi\u0119ksza kohezja \u2013 tym mocniej dana funkcja (klasa, modu\u0142) zorientowana jest na realizacj\u0119 jednego, konkretnego zadania. Perfekcyjnie kohezyjn\u0105 funkcj\u0105 jest funkcja sin(), spotykana w wielu j\u0119zykach i licz\u0105ca sinus z danej warto\u015bci. S\u0142u\u017cy ona do liczenia sinusa i nie ma \u017cadnych efekt\u00f3w ubocznych ani dodatkowego dzia\u0142ania. Podobn\u0105 kohezj\u0105 cechuj\u0105 si\u0119 funkcje zwane geterami lub seterami \u2013 ich zadaniem jest zwr\u00f3ci\u0107, lub ustawi\u0107 odpowiedni\u0105 warto\u015b\u0107 zapewniaj\u0105c odpowiedni poziom bezpiecze\u0144stwa.<\/p>\n<p>W naturze spotka si\u0119 wiele rodzaj\u00f3w kohezji \u2013 od najsilniejszej, funkcjonalnej, gdzie dzia\u0142anie danej funkcji jest \u015bci\u015ble podporz\u0105dkowane temu, co ma ona uczyni\u0107, a\u017c po kohezj\u0119 przypadkow\u0105, kt\u00f3rego to terminu nie trzeba chyba wyja\u015bnia\u0107. Istnieje wiele kohezji dobrych (np. kohezja sekwencyjna, wyst\u0119puj\u0105ca wtedy, gdy wa\u017cna jest kolejno\u015b\u0107 operacji, kt\u00f3re w innej kolejno\u015bci nie maj\u0105 sensu), jak i z\u0142ych. Jedn\u0105 ze z\u0142ych kohezji jest kohezja logiczna.<!--more--><\/p>\n<p>Podstawow\u0105 ide\u0105 kohezji logicznej jest tworzenie funkcji, kt\u00f3re wykonuj\u0105 r\u00f3\u017cne zadania, w zale\u017cno\u015bci od przes\u0142anej do nich flagi. Wykonywane funkcje s\u0105 \u015brednio ze sob\u0105 zwi\u0105zane, ot najwa\u017cniejsza jest owa steruj\u0105ca flaga. McConnell uwa\u017ca taki rodzaj kohezji za generalnie nieakceptowany.<\/p>\n<p>Pomimo, \u017ce generalnie zgadzam si\u0119 z McConnellem, to jednak dochodz\u0119 do wniosku, \u017ce myli si\u0119 on w tej sprawie. Z jednej strony mamy bowiem czysto\u015b\u0107 kodu, z drugiej za\u015b jego u\u017cyteczno\u015b\u0107. Przypomina mi to sytuacj\u0119, gdy <a href=\"http:\/\/stackoverflow.com\/questions\/956255\/why-is-it-bad-practice-to-call-an-eventhandler-from-code\/958253#958253\">mia\u0142em w funkcji obs\u0142ugi zdarzenia kod<\/a>, kt\u00f3ry w pewnym momencie prosi\u0142 u\u017cytkownika o potwierdzenie. Jaki\u015b czas p\u00f3\u017aniej musia\u0142em zautomatyzowa\u0107 t\u0105 formatk\u0119 zapewniaj\u0105c funkcj\u0119, kt\u00f3ra wykona to samo co wspomniana obs\u0142uga zdarzenia, klikaj\u0105c \u201eTak\u201d na zadawane w niej pytanie. Rozwi\u0105zaniem problemu by\u0142 refaktoring kodu obs\u0142uguj\u0105cego zdarzenie do nowej funkcji, kt\u00f3ra otrzyma\u0142a dodatkowy parametr m\u00f3wi\u0105cy czy pokazywa\u0107 potwierdzenie, czy domy\u015blnie zak\u0142ada\u0107 klikni\u0119cie \u201eTak\u201d. Funkcja nazywa\u0142a si\u0119 InternalSelectItem i jej wywo\u0142anie wyst\u0119powa\u0142o zar\u00f3wno w obs\u0142udze zdarzenia OnSelectItem (parametr m\u00f3wi\u0105cy o potwierdzeniu mia\u0142 warto\u015b\u0107 TRUE), oraz w obs\u0142udze automatycznej, gdzie z kolei parametr mia\u0142 warto\u015b\u0107 FALSE (d\u0142u\u017csz\u0105 dyskusj\u0119 mo\u017cna znale\u017a\u0107 na stackoverlow.com).<\/p>\n<p>Z drugiej strony niedawno w swej pracy stan\u0105\u0142em przed zadaniem dodania kilku funkcjonalno\u015bci na istniej\u0105cej formatce. Formatka prezentowa\u0142a r\u00f3\u017cne rodzaje byt\u00f3w (\u201eitem\u00f3w\u201d) kt\u00f3re mog\u0142y by\u0107 klasyfikowane na r\u00f3\u017cne sposoby. Istnia\u0142a ca\u0142a masa mo\u017cliwych zmian klasyfikacji dost\u0119pnych z menu kontekstowego. U\u017cytkownik zaznacza\u0142 sobie dane, kt\u00f3re chcia\u0142 zmieni\u0107, wybiera\u0142 z menu kontekstowego co chce zrobi\u0107, a system realizowa\u0142 jego \u017cyczenie.<\/p>\n<p>Oczywi\u015bcie zanim dochodzi\u0142o do wykonania\u00a0 operacji, u\u017cytkownik musia\u0142 potwierdzi\u0107 ch\u0119\u0107 wykonania, p\u00f3\u017aniej nale\u017ca\u0142o sprawdzi\u0107, czy zaznaczono prawid\u0142owe itemy, potem wykonywano operacj\u0119, a na ko\u0144cu od\u015bwie\u017cano list\u0119. Ca\u0142o\u015b\u0107 realizowana jest przez poni\u017cszy kod (tu w pseudopascalu):<\/p>\n<pre lang=\"delphi\" line=\"1\">procedure TMyForm.PerformOperationOnSelected (APerformingQuery: TSomeQuery; AQuestionText: string; ALimitOperationToAssignedOnly, ALimitOperationToIgnoredOnly:boolean);\r\nvar\r\n  li:integer;\r\n  lItemId: integer;\r\n  lMustRefresh:boolean;\r\nbegin\r\n  if Grid.SelectedRecordCount = 0 then Exit ;\r\n  if MessageDlg(AQuestionText, mtConfirmation, [mbYes, mbNo], 0) = mrNo then Exit;\r\n  lMustRefresh:=false;\r\n  { Iterating selected records }\r\n  for li := 0 to Grid.SelectedRecordCount - 1 do\r\n  begin\r\n    { If not a valid item, use continue to jump to next iteration}\r\n    if NOT Grid.SelectedRecords[ li ].IsValidItem then Continue ;\r\n    if (ALimitOperationToAssignedOnly) then\r\n    begin\r\n      {Use continue to jump to next iteration if unassigned or igonred}\r\n      if (NULL = Grid.SelectedRecords[ li ].Values[ cAssignmentId ]) then Continue;\r\n      if (cIgnored = Grid.SelectedRecords[ li ].Values[cAssignmentId]) then Continue;\r\n    end;\r\n    if (ALimitOperationToIgnoredOnly) then\r\n    begin\r\n      {Use continue to jump to next iteration if assigned or unassigned \u2013 ie limit to ignored}\r\n      if (NULL = Grid.SelectedRecords[ li ].Values[ cAssignmentId ]) then Continue;\r\n      if (0 &lt; Grid.SelectedRecords[ li ].Values[cAssignmentId]) then Continue;\r\n    end;\r\n    lItemId:= Grid.SelectedRecords[ li ].Values[ cItemId ] ;\r\n    APerformingQuery.ParamByName(cParamName).AsInteger:= lItemId;\r\n    APerformingQuery.Exeute();\r\n    lMustRefresh:=true;\r\n  end ;\r\n  if (lMustRefresh) then acRefresh.Execute();\r\nend;<\/pre>\n<p>C\u00f3\u017c tu si\u0119 dzieje? Ano najpierw mamy pytanie do u\u017cytkownika, nast\u0119pnie sprawdzenie kt\u00f3re Itemy s\u0105 w og\u00f3le w\u0142a\u015bciwe do przetwarzania (mo\u017ce si\u0119 okaza\u0107, \u017ce cz\u0119\u015b\u0107 wpis\u00f3w to jedynie wpisy grupuj\u0105ce). Dopiero p\u00f3\u017aniej w p\u0119tli dokonywane jest sprawdzenie ka\u017cdego itemu na okoliczno\u015b\u0107 pasowania do flagi steruj\u0105cej i je\u015bli w wyniku sprawdzenia nie zostaniemy skierowani na nast\u0119pn\u0105 iteracj\u0119, dokonywana jest w\u0142a\u015bciwa operacja za pomoc\u0105 obiektu <span style=\"font-family: Courier;\"><strong>APerformingQuery<\/strong><\/span> przekazywanego jako parametr. Procedura dodatkowo sprawdza, czy faktycznie dokona\u0142a jaki\u015b operacji, i wywo\u0142uje obiekt od\u015bwie\u017caj\u0105cy GUI.Jest to klasyczny przyk\u0142ad kohezji logicznej: nie do\u015b\u0107, \u017ce flagami steruj\u0105cymi znacz\u0105co zmieniamy zbi\u00f3r item\u00f3w wchodz\u0105cych w zakres dzia\u0142ania procedury, to jeszcze sam sens dzia\u0142ania \u2013 obiekt wykonuj\u0105cy dzia\u0142anie r\u00f3wnie\u017c jest przekazywany jako parametr! Jedyne, w czym si\u0119 procedura owa broni, to tekst komunikatu dla u\u017cytkownika, przekazany jako parametr \u2013 to mo\u017ce mie\u0107 sens cho\u0107by ze wzgl\u0119du na mo\u017cliwo\u015b\u0107 zmiany j\u0119zyka.<\/p>\n<p>Pozostaje zada\u0107 sobie pytanie, czy kod ten jest z\u0142y? C\u00f3\u017c, niewielka separacja logiki biznesowej od logiki interfejsu mo\u017ce jednak razi\u0107. Jednak bior\u0105c pod uwag\u0119, i\u017c faktyczna logika biznesowa ukryta jest w obiekcie wykonuj\u0105cym rzeczywiste dzia\u0142anie, sprawa przestaje wygl\u0105da\u0107 tak brzydko.<\/p>\n<p>\u015amierdz\u0105 troch\u0119 flagi steruj\u0105ce, ale\u2026 czy\u017c bez nich nie by\u0142oby konieczne implementowanie kilku r\u00f3\u017cnych procedur, do obs\u0142ugi ka\u017cdego elementu interfejsu u\u017cytkownika z osobna? Kohezja logiczna,\u00a0 mimo \u017ce generalnie niezbyt akceptowalna, potrafi czasem pom\u00f3c nam, zmniejszaj\u0105c ilo\u015b\u0107 kodu obs\u0142uguj\u0105cego interfejs u\u017cytkownika i drastycznie zmniejszaj\u0105c ilo\u015b\u0107 punkt\u00f3w styku logiki interfejsu i logiki biznesowej.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>W swej znakomitej ksi\u0105\u017cce \u201eCode Complete\u201d Steven McConnell rozwa\u017ca mi\u0119dzy innymi poj\u0119cie kohezji.\u00a0 W du\u017cym skr\u00f3cie \u2013 kohezja jest miar\u0105 \u201espoisto\u015bci\u201d danego modu\u0142u, klasy lub funkcji. Im wi\u0119ksza kohezja \u2013 tym mocniej dana funkcja (klasa, modu\u0142) zorientowana jest na realizacj\u0119 jednego, konkretnego zadania. Perfekcyjnie kohezyjn\u0105 funkcj\u0105 jest funkcja sin(), spotykana w wielu j\u0119zykach i licz\u0105ca &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/bielamowicz.info\/index.php\/2009\/kohezja-logiczna\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;W obronie kohezji logicznej&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-37","post","type-post","status-publish","format-standard","hentry","category-informatyka"],"_links":{"self":[{"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/posts\/37","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/comments?post=37"}],"version-history":[{"count":1,"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/posts\/37\/revisions"}],"predecessor-version":[{"id":196,"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/posts\/37\/revisions\/196"}],"wp:attachment":[{"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/media?parent=37"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/categories?post=37"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/bielamowicz.info\/index.php\/wp-json\/wp\/v2\/tags?post=37"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}