0% found this document useful (0 votes)
8 views111 pages

PR02

The document discusses object-oriented programming (OOP) design principles, focusing on the importance of clean code and the S.O.L.I.D. principles for maintaining good design. It covers concepts such as inheritance, polymorphism, abstract classes, and interfaces, along with examples in C#. The document emphasizes the need for refactoring to avoid bad practices and improve code maintainability.

Uploaded by

Dejan Radosevic
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views111 pages

PR02

The document discusses object-oriented programming (OOP) design principles, focusing on the importance of clean code and the S.O.L.I.D. principles for maintaining good design. It covers concepts such as inheritance, polymorphism, abstract classes, and interfaces, along with examples in C#. The document emphasizes the need for refactoring to avoid bad practices and improve code maintainability.

Uploaded by

Dejan Radosevic
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 111

Dizajn u objektno orijentiranom programiranju

Fakultet elektrotehnike, računarstva i informacijskih tehnologija Osijek

Predavanje 2 – (Ne)Čist kôd

(FERIT) DOOP PR 1 / 111


Uvod

(FERIT) DOOP PR 2 / 111


Uvod Pregled predavanja

Nečist kôd
Kôd koji narušava (dobar) objektno orijentiran dizajn

Uporaba objektno orijentiranih načela u razvoju služe za postizanje


dobrog dizajna
◦ S.O.L.I.D. načela
Održavanje ili postizanje dobrog dizajna kroz refaktoriranje je ključno
kako bi se izbjegle ili uklonile loše prakse/pristupi
◦ Mirisi u kôdu
◦ Anti-obrasci
◦ S.T.U.P.I.D. kôd

(FERIT) DOOP PR 3 / 111


Uvod Pregled OOP

Pregled potrebnih koncepata OOP-a


Nasljed̄ivanje i polimorfizam

◦ Osnovna klasa
◦ Izvedene klase
◦ Apstraktne klase
◦ Sučelja

(FERIT) DOOP PR 4 / 111


Uvod Pregled OOP

Nasljeðivanje
Omogućuje izgradnju hijerarhije klasa putem veza tipa JE (engl. "is a")

◦ Izvesti ili naslijediti klasu znači specijalirazirati ju dodavanjem


članova (podatkovnih i/ili funkcijskih) ili izmjenom postojećeg
ponašanja
◦ Klasa koju se nasljed̄uje naziva se osnovnom ili nadklasom (engl.
base, super class)
◦ Klasa koja nasljed̄uje osnovnu naziva se izvedenom i podklasom
(engl. derived, sub class)
◦ U programskom jeziku C# moguće je samo jednu klasu izravno
naslijediti
◦ ... sve klase u jeziku (implicitno) nasljed̄uju klasu Object (u imeniku
System)

(FERIT) DOOP PR 5 / 111


Uvod Pregled OOP

1 class BaseClass
2 {
3 private int _ID = 0;
4 protected string FirstName = "Base";
5 protected string LastName = " Class ";
6
7 public string GetName () {
8 return this. FirstName + this. LastName ;
9 }
10 public int ID { get { return this._ID; } }
11 }
12
13 class DerivedClass : BaseClass
14 {
15 public DerivedClass () {
16 this. FirstName = " Derived ";
17 }
18 // skriva metodu osnovne klase
19 public new string GetName () { // kljucna rijec new nije obavezna
20 return this. FirstName + " " + this. LastName ;
21 }
22 }

(FERIT) DOOP PR 6 / 111


Uvod Pregled OOP

1 class Program
2 {
3 static void Main ()
4 {
5 BaseClass Super = new BaseClass ();
6 DerivedClass Sub = new DerivedClass ();
7
8 Console . WriteLine ("Base class name: " + Super . GetName ()
9 + " and ID: " + Super .ID );
10 Console . WriteLine (" Derived class name: " + Sub. GetName ()
11 + " and ID: " + Sub.ID );
12 }
13 }

(FERIT) DOOP PR 7 / 111


Uvod Pregled OOP

◦ Ako je klasa zatvorena nije ju moguće naslijediti


1 sealed class BaseClass { ... }

◦ Apstraktne klase služe samo za potrebe nasljed̄ivanja


◦ ... služe za dijeljenje zajedničih članova
◦ ... često ne implementiraju veći dio funkcionalnosti/ponašanja
◦ ... nije ih moguće instancirati, za razliku od konkretnih klasa
◦ ... konkretne izvedene klase moraju implementirati apstraktne
članove (metode) osnovne klase
1 abstract class BaseClass { ... }

(FERIT) DOOP PR 8 / 111


Uvod Pregled OOP

1 abstract class AbstractClass {


2 public string Name { get; private set; }
3 public AbstractClass ( string name) {
4 this.Name = name;
5 }
6 public abstract string GetSummary ();
7 }
8 class DerivedClass : AbstractClass {
9 public DerivedClass () : base(" Derived ") { }
10 // kljucna rijec override je obavezna
11 public override string GetSummary () {
12 return " Summary data: " + this.Name;
13 }
14 }

1 class Program {
2 static void Main () {
3 DerivedClass Sub = new DerivedClass ();
4 Console . WriteLine (Sub. GetSummary ());
5 }
6 }

(FERIT) DOOP PR 9 / 111


Uvod Pregled OOP

Polimorfizam
Mogućnost da neki objekt poprimi više različitih oblika

◦ Polimorfizam se odnosi na postojanje više različitih


implementacija metode (osnovne klase) s istim potpisom u
različitim (izvedenim) klasama
◦ pretpostavka je kako pojedini objekti najbolje znaju sami kako
obaviti neki zadatak
◦ Virtualni i apstraktni članovi su jedan mehanizam koji omogućuje
polimorfizam

(FERIT) DOOP PR 10 / 111


Uvod Pregled OOP

1 abstract class BaseClass {


2 private int _ID = 0;
3 protected string FirstName = "Base";
4 protected string LastName = " Class ";
5
6 public abstract string GetName ();
7 public virtual int ID { get { return this._ID; } }
8 public override string ToString () { // iz klase Object
9 return this. FirstName + " " + this. LastName
10 + "| ID: " + this.ID;
11 }
12 }
13
14 class DerivedClass : BaseClass {
15 public DerivedClass () {
16 this. FirstName = " Derived ";
17 }
18 public override string GetName () {
19 return this. FirstName + " " + this. LastName ;
20 }
21 public override int ID { get { return base.ID + 1; } }
22 }

(FERIT) DOOP PR 11 / 111


Uvod Pregled OOP

1 class Program
2 {
3 static void Main ()
4 {
5 DerivedClass Sub = new DerivedClass ();
6 BaseClass Super = Sub;
7
8 Console . WriteLine ( Super );
9 Console . WriteLine (Sub );
10 }
11 }

◦ Za razliku od jezika Java, jezik C# ne dopušta implicitno


prepisivanje (engl. overriding)
◦ osnovna i izvedena klasa se moraju slagati i koristiti ključnu riječ
virtual (ili abstract), odnosno override

(FERIT) DOOP PR 12 / 111


Uvod Pregled OOP

1 abstract class Base


2 {
3 public abstract string GetName ();
4 }
5
6 class DerivedOne : Base
7 {
8 public override string GetName () { return " First derived "; }
9 }
10
11 class DerivedTwo : Base
12 {
13 public override string GetName () { return " Second derived "; }
14 }
15
16 class DerivedThree : Base
17 {
18 public override string GetName () { return " Third derived "; }
19 }

(FERIT) DOOP PR 13 / 111


Uvod Pregled OOP

1 class Program
2 {
3 static void Main ()
4 {
5 Base [] baseForms = new Base [] {
6 new DerivedOne (),
7 new DerivedTwo (),
8 new DerivedThree ()};
9
10 foreach (Base form in baseForms ) {
11 string name = form. GetName ();
12 Console . WriteLine (name );
13 }
14 }
15 }

(FERIT) DOOP PR 14 / 111


Uvod Pregled OOP

◦ Polimorfizam nije samo omogućen putem nasljed̄ivanja, nego i


putem sučelja (engl. interfaces)
◦ Za razliku od apstraktnih klasa, sučelja ne mogu uključivati nikakvu
implementaciju ili podatke
◦ Sučelja ne podržavaju pristupne modifikatore (sve metode su
automatski javne)
◦ Sučelje definira ugovor izmed̄u klasa koje ga ugrad̄uju i klasa koje
ga koriste
◦ Ugradnjom sučelja klasa se obavezuje implementirati sve metode
koje definira sučelje
◦ Jedna klasa može ugraditi više različitih sučelja
◦ Ugradnja sučelja predstavlja vezu tipa MOŽE (engl. "can do")

(FERIT) DOOP PR 15 / 111


Uvod Pregled OOP

1 interface INameProvider
2 {
3 string GetFirstName ();
4 string GetLastName ();
5 }
6
7 class Person : INameProvider
8 {
9 private string FirstName ;
10 private string LastName ;
11 public Person ( string name) {
12 string [] names = name. Split (’ ’);
13 this. FirstName = names [0];
14 this. LastName = names [1];
15 }
16 public string GetFirstName () {
17 return this. FirstName ;
18 }
19 public string GetLastName () {
20 return this. LastName ;
21 }
22 }

(FERIT) DOOP PR 16 / 111


Uvod Pregled OOP

1 class Program
2 {
3 static void Main ()
4 {
5 Person person = new Person ("Otto Hermann ");
6
7 PrintName ( person );
8 }
9
10 static void PrintName ( INameProvider name)
11 {
12 string firstName = name. GetFirstName ();
13 string lastName = name. GetLastName ();
14 Console . WriteLine ( firstName + " " + lastName );
15 }
16 }

(FERIT) DOOP PR 17 / 111


S.O.L.I.D. načela

(FERIT) DOOP PR 18 / 111


S.O.L.I.D. načela

S.O.L.I.D. načela
Pet važnih načela objektno orijentiranog razvoja čija je svrha učiniti
tekst programa jednostavnijim za održavanje i proširivanje

◦ Single Responsibility Principle (SRP)


◦ Open/Closed Principle (OCP)
◦ Liskov Substitution Principle (LSP)
◦ Interface Segregation Principle (ISP)
◦ Dependency Inversion Principle (DIP)

(FERIT) DOOP PR 19 / 111


S.O.L.I.D. načela SRP

Svaka klasa treba imati samo jednu odgovornost, odnosno


odgovornost za jednu funkcionalnost
◦ Samo jedan razlog treba postojati za izmjenu klase
◦ ... više odgovornosti znači i više potreba za eventualnim izmjenama
iz različitih razloga
◦ ... izmjena jedne odgovornosti može za sobom povlačiti izmjene
ostalih
◦ ... izmjena jedne odgovornosti može za sobom povlačiti
nemogućnost održavanja ostalih
◦ Jedna odgovornost (može biti više metoda) u pravilu rezultira
manjim i jednostavnijim klasama
◦ ... jednostavnije za razumijeti, koristiti, održavati i testirati

(FERIT) DOOP PR 20 / 111


S.O.L.I.D. načela SRP

1 class Contact {
2 private int _ID;
3 public string FirstName { get; private set; }
4 public string LastName { get; private set; }
5 ...
6 }
7
8 class PhoneBook {
9 private List <Contact > contacts ;
10
11 // odgovornost
12 public void Insert ( Contact c, string logMessage ) {
13 ...
14 LogInsertion ( logMessage );
15 }
16
17 // jos jedna odgovornost , ali ne bi trebala biti od klase PhoneBook
18 public void LogInsertion ( string message ) { ... }
19 }

(FERIT) DOOP PR 21 / 111


S.O.L.I.D. načela SRP

1 class Contact {
2 ...
3 }
4
5 class PhoneBook {
6 private List <Contact > contacts ;
7
8 public void Insert ( Contact c) {
9 ...
10 }
11 }
12
13 // neprikladna odgovornost izdvojena u posebnu klasu
14 class LogService
15 {
16 public void Log( Contact contact , string message ) { ... }
17 }

(FERIT) DOOP PR 22 / 111


S.O.L.I.D. načela SRP

1 class Contact {
2 ...
3 }
4
5 class PhoneBook {
6 private List <Contact > contacts ;
7
8 public void Insert ( Contact c) {
9 ...
10 }
11
12 // treba li biti odgovornost klase PhoneBook ?
13 public void ExportToXML () { ... }
14 }

(FERIT) DOOP PR 23 / 111


S.O.L.I.D. načela SRP

1 class Contact {
2 ...
3 }
4
5 class PhoneBook {
6 private List <Contact > contacts ;
7
8 public void Insert ( Contact c) {
9 ...
10 }
11 }
12
13 // neprikladna odgovornost izdvojena u posebnu klasu
14 class ContactsExporter {
15 ...
16 public void ExportToXML ( PhoneBook contacts ) { ... }
17 }

(FERIT) DOOP PR 24 / 111


S.O.L.I.D. načela OCP

Klase trebaju biti zatvorene za izmjene, ali otvorene za


proširenja
◦ Trebalo bi biti moguće proširiti ponašanje bez izmjene "temelja"
(izvornog kôda)
◦ ... jednostavnije za održavati
◦ "Otovorenost za proširenje" znači da je ponašanje (klase) moguće
proširiti – moguće je ostvariti novo i/ili drugačije ponašanje kako bi
se zadovoljili zahtjevi
◦ "Zatvorenost za izmjene" znači da nikakve izmjene/promjene
(izvornog kôda klase) nisu dozvoljene.

(FERIT) DOOP PR 25 / 111


S.O.L.I.D. načela OCP

◦ Nasljed̄ivanje i polimorfizam se koristi za ostvarenje navedenog


◦ ključ je apstrakcija koja je fiksna, ali omogućuje ostvariranje bezbroj
različitih ponašanja kroz implementacije
◦ ... apstrakciju predstavlja apstraktna osnovna klasa ili sučelje, a
različita ponašanja predstavljaju izvedene klase
◦ ... izmjene programa kroz dodavanje novog kôda, a ne izmjenom
postojećeg kôda

(FERIT) DOOP PR 26 / 111


S.O.L.I.D. načela OCP

Uporaba naredbe switch često ukazuje na kršenje OCP-a


1 class Contact
2 {
3 ...
4 private string Type;
5 ...
6
7 public string GetIconColor () {
8 switch (this.Type) {
9 case "Home": return " yellow ";
10 case " Mobile ": return " green ";
11 }
12 }
13 }

(FERIT) DOOP PR 27 / 111


S.O.L.I.D. načela OCP

1 abstract class Contact


2 {
3 ...
4 public abstract string GetIconColor ();
5 }
6
7 // prosirenje bez izmjene " temelja ", odnosno klase Contact
8 class HomeContact : Contact
9 {
10 ...
11 public override string GetIconColor () { return " yellow "; }
12 }
13
14 class MobileContact : Contact
15 {
16 ...
17 public override string GetIconColor () { return " green "; }
18 }

(FERIT) DOOP PR 28 / 111


S.O.L.I.D. načela OCP

Problem može biti i u posrednoj klasi


1 class LogService
2 {
3 public void Log( Contact contact , string message ) {
4 string header ;
5 switch ( contact .Type) {
6 case "Home": header = "HOME: "; break ;
7 case " Mobile ": header = " MOBILE : "; break ;
8 }
9
10 Console . WriteLine ( header + message );
11 }
12 }

(FERIT) DOOP PR 29 / 111


S.O.L.I.D. načela OCP

1 interface ILogable
2 {
3 string GetMessageHeader ();
4 }
5
6 abstract class Contact
7 {
8 ...
9 }
10
11 class HomeContact : Contact , ILogable
12 { // eksplicitna ugradnja sucelja
13 string ILogable . GetMessageHeader () { return "HOME: "; }
14 }
15 class MobileContact : Contact , ILogable
16 { // eksplicitna ugradnja sucelja
17 string ILogable . GetMessageHeader () { return " MOBILE : "; }
18 }

(FERIT) DOOP PR 30 / 111


S.O.L.I.D. načela OCP

1 class LogService
2 {
3 public void Log( ILogable obj , string message ) {
4 string header = obj. GetMessageHeader ();
5 Console . WriteLine ( header + message );
6 }
7 }

(FERIT) DOOP PR 31 / 111


S.O.L.I.D. načela LSP

Izvedene klase trebaju moći zamijeniti osnovnu


◦ Ako je Sub podtip od Sup, onda treba biti moguće zamijeniti
objekte tipa Sup objektima tipa Sub bez narušavanja ponašanja
Sup
◦ ... metode koje imaju reference na osnovnu klasu trebaju moći
koristiti objekte izvedenih klasa bez da su toga "svjesne"
◦ Izvedene klase ne smiju narušavati ponašanje osnovne klase
◦ ... program treba jednako raditi i nakon zamjene
◦ Ovo načelo (LSP) i OCP su usko vezani
◦ ... kršenje LSP dovodi do kršenja OCP

(FERIT) DOOP PR 32 / 111


S.O.L.I.D. načela LSP

1 class TextFile
2 {
3 public virtual string Read () { ... }
4 public virtual void Write ( string text) { ... }
5 }
6
7 class ReadOnlyTextFile : TextFile
8 {
9 public override string Read () { ... }
10 public override void Write ( string text) {
11 throw new NotImplementedException ();
12 }
13 }

(FERIT) DOOP PR 33 / 111


S.O.L.I.D. načela LSP

1 class TextFileManager
2 {
3 // klijent zna samo za klasu TextFile
4 List <TextFile > files ;
5 public Files { set { files = value ; } }
6
7 public string LinkTextFiles () {
8 StringBuilder linkedFiles = new StringBuilder ();
9 foreach ( TextFile file in files ) {
10 linkedFiles . Append (file.Read ());
11 }
12 return linkedFiles ;
13 }
14
15 // klijent zna da klasa TextFile pruza citanje i pisanje
16 public void ModifyTextFiles ( string text) {
17 foreach ( TextFile file in files ) {
18 file. Write (text );
19 }
20 }
21 }

(FERIT) DOOP PR 34 / 111


S.O.L.I.D. načela LSP

1 class TextFileManager
2 {
3 // klijent zna samo za klasu TextFile
4 List <TextFile > files ;
5 public Files { set { files = value ; } }
6
7 public string LinkTextFiles () {
8 StringBuilder linkedFiles = new StringBuilder ();
9 foreach ( TextFile file in files ) {
10 linkedFiles . Append (file.Read ());
11 }
12 return linkedFiles ;
13 }
14
15 //" rjesenje " koje krsi OCP
16 public void ModifyTextFiles ( string text) {
17 foreach ( TextFile file in files ) {
18 if (file. GetType () != typeof ( ReadOnlyTextFile ))
19 file. Write (text );
20 }
21 }
22 }

(FERIT) DOOP PR 35 / 111


S.O.L.I.D. načela LSP

1 interface IReadable
2 {
3 string Read ();
4 }
5
6 interface IWritable
7 {
8 void Write ( string text );
9 }

1 class TextFile : IReadable , IWriteable


2 {
3 public string Read () { ... }
4 public void Write ( string text) { ... }
5 }
6
7 class ReadOnlyTextFile : IReadable
8 {
9 public string Read () { ... }
10 }

(FERIT) DOOP PR 36 / 111


S.O.L.I.D. načela LSP

1 class TextFileManager
2 {
3 public string LinkTextFiles (List <IReadable > files ) {
4 StringBuilder linkedFiles = new StringBuilder ();
5 foreach ( IReadable file in files ) {
6 linkedFiles . Append (file.Read ());
7 }
8 return linkedFiles ;
9 }
10
11 public void ModifyTextFiles (List <IWritable > files , string text) {
12 foreach ( IWritable file in files ) {
13 file. Write (text );
14 }
15 }
16 }

(FERIT) DOOP PR 37 / 111


S.O.L.I.D. načela ISP

Ne treba kroz ugradnju sučelja forsirati implementaciju


metoda koje se ne koriste ili nisu potrebne ili nisu valjane
za danu klasu
◦ Poželjno je imati više manjih sučelja u odnosu na jedno (ili
nekoliko) veliko
◦ ... ako nekoj klasi ne treba neka metoda, nije dobro da ju
implementira samo zato što to propisuje sučelje
◦ ... što je sučelje veće (ima više metoda) veća je vjerojatnost kako
ga neka klasa neće moći u potpunosti implementirati
◦ ... med̄uovisnost klasa je manja u slučaju manjih sučelja

(FERIT) DOOP PR 38 / 111


S.O.L.I.D. načela ISP

◦ Veliko sučelje često je moguće razlomiti na manja, odnosno na


manje grupe metoda
◦ ... pojedine grupe trebaju služiti različitim skupinama "klijenata"
◦ ... razdvajanje/razlamanje sučelja znači razdvajanje "klijenata"
◦ ... klijenti ne trebaju ovisiti o sučeljima koja ne koriste
◦ Ovo načelo (ISP) pridonosi održavanju LSP

(FERIT) DOOP PR 39 / 111


S.O.L.I.D. načela ISP

1 interface IProcessable
2 {
3 string Read ();
4 void Write ( string text );
5 }
6
7 class TextFile : IProcessable
8 {
9 public string Read () { ... }
10 public void Write ( string text) { ... }
11 }
12
13 class ReadOnlyTextFile : IProcessable
14 {
15 public string Read () { ... }
16 public void Write ( string text) {
17 throw new NotImplementedException ();
18 }
19 }

(FERIT) DOOP PR 40 / 111


S.O.L.I.D. načela ISP

1 interface IReadable
2 {
3 string Read ();
4 }
5
6 interface IWritable
7 {
8 void Write ( string text );
9 }
10
11 class TextFile : IReadable , IWriteable
12 {
13 public string Read () { ... }
14 public void Write ( string text) { ... }
15 }
16
17 class ReadOnlyTextFile : IReadable
18 {
19 public string Read () { ... }
20 }

(FERIT) DOOP PR 41 / 111


S.O.L.I.D. načela ISP

1 class Contact {
2 public Name { get; set; }
3 public Address { get; set; }
4 public PhoneNumber { get; set; }
5 public EmailAddress { get; set; }
6 }

1 class EmailService { // klijent - treba mu samo ime i email adresa


2 ...
3 public void SendEmail ( Contact contact ,
4 string subject , string body) {
5 ...
6 }
7 }
8
9 class PhoneService { // klijent - treba mu samo broj telefona
10 ...
11 public void MakeCall ( Contact contact ) { ... }
12 public void SendSMS ( Contact contact , string text) { ... }
13 }

(FERIT) DOOP PR 42 / 111


S.O.L.I.D. načela ISP

1 interface IEmailable {
2 string Name { get; set; }
3 string EmailAddress { get; set; }
4 }
5
6 interface IPhoneable {
7 string PhoneNumber { get; set; }
8 }
9
10 class Contact : IEmailable , IDiallable {
11 public Name { get; set; }
12 public Address { get; set; }
13 public PhoneNumber { get; set; }
14 public EmailAddress { get; set; }
15 }

(FERIT) DOOP PR 43 / 111


S.O.L.I.D. načela ISP

1 // klijent - rukuje samo s njemu nuznim dijelom kontakta


2 class EmailService {
3 ...
4 public void SendEmail ( IEmailable contact ,
5 string subject , string body) {
6 ...
7 }
8 }
9
10 // klijent - rukuje samo s njemu nuznim dijelom kontakta
11 class PhoneService {
12 ...
13 public void MakeCall ( IPhoneable contact ) { ... }
14 public void SendSMS ( IPhoneable contact , string text) { ... }
15 }

(FERIT) DOOP PR 44 / 111


S.O.L.I.D. načela DIP

Klase na višim razinama ne trebaju ovisiti o klasama na nižim


razinama, nego (oboje) trebaju ovisiti o apstrakciji
◦ Klase na višim razinama uglavnom koriste ponašanje onih na
nižim razinama
◦ klase na višim razinama ugrad̄uju pravila ponašanja i logiku sustava
◦ klase na nižim razinama ugrad̄uju detaljnije zadatke

(FERIT) DOOP PR 45 / 111


S.O.L.I.D. načela DIP

◦ Ako klasa na višoj razini ovisi o onoj na nižoj, kaže se kako su


usko povezane (engl. tightly coupled)
◦ ... jedna se oslanja na definiciju druge klase
◦ ... promjene jedne mogu narušiti ispravnost druge klase
◦ ... ograničena je ponovna uporaba klasa na višim razinama
◦ ... izmjene klasa na nižim razinima mogu prisiliti izmjene klasa na
višim razinama
◦ Treba ovisiti o apstrakcijama, a ne o konkretnim klasama
◦ ... konkretne klase se često mijenjaju, a apstrakcije ne

(FERIT) DOOP PR 46 / 111


S.O.L.I.D. načela DIP

1 class Email
2 {
3 public void Send( string message )
4 }
5
6 class NotificationService
7 {
8 Email email ;
9 // sto ako bude potreban drugi oblik dostave poruka ?
10 public NotificationService () {
11 email = new Email ();
12 }
13
14 public void Broadcast ( string message ) {
15 ...
16 email .Send( message );
17 }
18 }

(FERIT) DOOP PR 47 / 111


S.O.L.I.D. načela DIP

◦ Teži se labavoj vezi (engl. loosely coupled) izmed̄u klasa na


višima razinama i onih na nižim
◦ ... uvod̄enjem ovisnosti o apstraciji, a može se postići na različite
načine

1 interface IMessenger
2 {
3 void Send( string message );
4 }
5
6 class Email : IMessenger
7 {
8 public void Send( string message ) { ... }
9 }
10
11 class SMS : IMessenger
12 {
13 public void Send( string message ) { ... }
14 }

(FERIT) DOOP PR 48 / 111


S.O.L.I.D. načela DIP

◦ Ubrizgavanje ovisnosti (engl. dependency injection) kroz


konstruktor

1 class NotificationService
2 {
3 IMessenger MessagingService ;
4
5 public NotificationService ( IMessenger service ) {
6 this. MessagingService = service ;
7 }
8
9 public void Broadcast ( string message ) {
10 ...
11 MessagingService .Send( message );
12 }
13 }

(FERIT) DOOP PR 49 / 111


S.O.L.I.D. načela DIP

◦ Ubrizgavanje ovisnosti kroz svojstvo (engl. property)

1 class NotificationService
2 {
3 IMessenger _MessagingService ;
4
5 public IMessenger MessagingService {
6 set { _MessagingService = value ; }
7 private get { return this. _MessagingService ; }
8 }
9
10 public void Broadcast ( string message ) {
11 ...
12 MessagingService .Send( message );
13 }
14 }

(FERIT) DOOP PR 50 / 111


S.O.L.I.D. načela DIP

◦ Ubrizgavanje ovisnosti kroz metodu

1 class NotificationService
2 {
3 public void Broadcast ( IMessenger service , string message ) {
4 ...
5 service .Send( message );
6 }
7 }

(FERIT) DOOP PR 51 / 111


S.O.L.I.D. načela Sažetak

Primjena S.O.L.I.D. načela u razvoju omogućuje pisanje kôda koji je


jednostavan za održavanje i proširivanje
◦ Svaka klasa treba imati samo jednu odgovornost, odnosno treba
postojati samo jedan razlog za njenu promjenu
◦ ... klasa treba pokrivati/enkapsulirati samo jednu funkcionalnost
sustava
◦ Klase trebaju biti zatvorene za promjene, ali otvorene za
proširenje
◦ ... ponašanje treba biti moguće proširiti bez izmjene postojećeg
kôda (nasljed̄ivanjem se ne mijenja kôd osnovne klase)

(FERIT) DOOP PR 52 / 111


S.O.L.I.D. načela Sažetak

◦ Objekte izvedenih klasa treba biti moguće zamijeniti objektom


osnovne klase
◦ ... zamjena ne smije utjecati na ispravnost programa
◦ Ne treba forsirati implementaciju suvišnih metoda kroz sučelja
◦ ... ako je nekoj klasi dana metoda suvišna, ne treba njenu
implementaciju siliti (razbiti veliko sučelje na manja)
◦ Klase na višim razinama i one na nižim trebaju ovisiti o istoj
apstrakciji
◦ ... klasa na višim razinama treba ovisiti o apstrakciji, a ne o
konkretnoj klasi

(FERIT) DOOP PR 53 / 111


Mirisi u kôdu

(FERIT) DOOP PR 54 / 111


Mirisi u kôdu

Mirisi u kôdu (engl. code smells, bad smells in code)


Lako učiljivi pokazatelji na (dublje) probleme u sustavu

◦ Mirisi su odred̄ene strukture u kôdu koje ukazuju na kršenje


dobrih načela u razvoju/dizajnu programske podrške
◦ Postoje brojni mirisi koji upućuju na potencijalne probleme
◦ duplicirani kôd ◦ privremeni atributi
◦ velike funkcije/metode ◦ divergentne promjene
◦ velike klase ◦ operacije sačmarom
◦ metode s brojnim parametrima ◦ podatkovne klase
◦ zavidne metode ◦ opsežna uporaba komentara
◦ nakupine podataka ◦ neprimjerena bliskost
◦ odbijeno nasljedstvo ◦ naredba switch
◦ lijene klase ◦ čovjek u sredini

(FERIT) DOOP PR 55 / 111


Mirisi u kôdu

◦ Sam miris ne mora biti problem, ali može ukazivati na postojanje


problema
◦ ... jednostavno su uočiljivi, ali nije uvijek jednostavno odrediti radi li
se o problemu i ako se radi, kako ga ispraviti
◦ Mirisi se odnose na to kada bi trebalo provesti refaktoriranje kôda

(FERIT) DOOP PR 56 / 111


Mirisi u kôdu Duplicirani kôd

Dupliciranje kôda treba izbjegavati pod svaku cijenu i


tražiti načine za njegovo ujedinjavanje
◦ Ako postoji isti kôd u dvije metode jedne klase
◦ ... izdvojiti taj kôd u novu metodu i korisiti ju na oba mjesta
◦ Ako postoje iste metode u izvedenim klasama jedne osnovne
klase
◦ ... izdvojiti u jednu metodu i premjestiti ju u osnovnu klasu
◦ Ako postoje metode (u izvedenim klasama) koje rade isti zadatak,
ali prema različitim algoritmima
◦ ... odabrati jednostavniji/jasniji

(FERIT) DOOP PR 57 / 111


Mirisi u kôdu Duplicirani kôd

◦ Ako postoje metode u izvedenim klasama koje rade na sličan


način
◦ ... razdvojiti korake u metode tako da originalne postanu jednake te
potom ih premjesiti u osnovnu klasu
◦ Ako postoji dupliciranje u dvijema klasama koje nisu povezane
◦ ... možda ta metoda pripada samo u jednu klasu, a druga ju treba
samo pozivati
◦ ... možda ta metoda pripada u treću klasu na koju se trebaju
oslanjati prve dvije

(FERIT) DOOP PR 58 / 111


Mirisi u kôdu Velike funkcije/metode

Malene metode su bolje u odnosu na velike u smislu


razumljivosti i korištenja
◦ Malene metode ne zahtijevaju komentare
◦ ... često je moguće zamijeniti komentar metodom odgovarajućeg
imena
◦ ... komentari koji opisuju kako/što neki dio radi često ukazuju na dio
kojeg se može izdvojiti kao posebnu metodu
◦ Dijelove većih metoda koji prirodno idu zajedno u pravilu je
moguće izdvojiti kao posebnu metodu
◦ Ako metoda (koju se želi razlomiti) ima brojne privremene
varijable u koje se spremaju rezultati izraza
◦ ... izdvojiti izraz u posebnu metodu i zamijeniti pojave izraza,
odnosno privremene varijable izdvojenom metodom

(FERIT) DOOP PR 59 / 111


Mirisi u kôdu Velike funkcije/metode

◦ Ako izdvajanje metode rezultira metodom s brojnim parametrima


◦ ... parametre koji idu prirodne skupa zamijeniti odgovarajućim
objektom kojeg se može predati metodi kao argument
◦ Ako je izdvajanje problematično zbog brojnih privrmenenih
varijabli i rezultiralo bi brojim parametrima
◦ ... napraviti klasu s metodom koja drži dio kôda kojeg se želi
izdvojiti, a nužne privremene varijable staviti kao atribute te klase
◦ Mogućnost izdvajanja metoda pruža se i u slučaju složenih
grananja
◦ ... izdvojiti uvjet kao metodu, a isto postupiti s naredbama koji su
pod njim

(FERIT) DOOP PR 60 / 111


Mirisi u kôdu Velike klase

Iza velikih klasa nerijetko se krije duplicirani i/ili zamršeni


kôd te veliki broj objekata te klase
◦ Velike klase uobičajeno pokušavaju obavljati puno poslova
◦ ... često imaju veliki broj atributa i brojne (velike) metode
◦ Ako klasa ima brojne atribute
◦ ... vjerojatno ju je moguće razdvojiti na više manjih klasa ili izvesti
podklase iz nje
◦ ... atributi čija imena imaju zajedničke dijelove često ukazuju na
mogućnost razdvajanja
◦ ... ako ima atribute koje ne koristi uvijek, vjerojatno ih je moguće
izdvojiti u posebnu klasu ili podklasu
◦ Ako klasa ima velike metode
◦ ... razlomiti te metode na manje i ukloniti eventualnu redundanciju
◦ Ako više "klijenta" koristi isti podskup metoda
◦ ... izdvojiti taj podskup kao sučelje čime je omogućeno grupiranje
metoda

(FERIT) DOOP PR 61 / 111


Mirisi u kôdu Metode s brojnim parametrima

Veliki broj parametara uglavnom upućuje na veliku


složenost metode
◦ Metode s velikim brojem parametara su uobičajeno teške za
razumijeti i teške za koristiti
◦ ... dodatnu otežavajuću okolnost predstavljaju i izmjene parametara
takvih metoda
◦ Ako se poziva metoda (bez parametara) na objektu i rezultat se
predaje drugoj metodi kao argument
◦ ... ukloniti parametar iz druge metode i prepustiti joj poziv prve
◦ Ako se metodi kao argumenti predaje više podataka dobivenih iz
jednog objekta
◦ ... predati joj cijeli objekt
◦ ... ukoliko se iz odred̄enih razloga ne želi stvoriti ovisnost jedne
klase o drugoj onda treba prethodno izbjegavati

(FERIT) DOOP PR 62 / 111


Mirisi u kôdu Zavidne metode

Metode koje primarno koriste podatke iz neke druge klase1


vjerojatno bi trebale i pripadati njoj
◦ Takve metode su zavidne na podatke druge klase
◦ ... prebaciti metodu u klasu čije podatke koristi
◦ Ako samo jedan dio metode koristi podatke druge klase
◦ ... izdvojiti taj dio u posebnu metodu i premijestiti ju
◦ Ako je metoda zavidna na podatke nekoliko drugih klasa
◦ ... prebaciti ju u onu klasu koja ima najviše podataka
◦ ... ako je moguće, razlomiti metodu na više manjih i prebaciti ih u
klase čije podatke koriste

1
Zavidna metoda (engl. feature envy)
(FERIT) DOOP PR 63 / 111
Mirisi u kôdu Nakupine podataka

Pojave nakupine/grupe podataka na nekoliko različitih


mjesta u kôdu2 nisu rijetke
◦ Nakupine podataka kao što su isti atributi u nekoliko klasa te isti
broj i tipovi parametara u različitim metodama nisu neuobičajeni
◦ Ako se radi o istim atributima u dvije ili više klasa
◦ ... može ih se uvesti kao novu klasu
◦ Ako se radi o metodama s istim parametrima
◦ ... uvesti novu klasu i njene objekte koristiti kao argumente
◦ ... ako metode koriste više podataka jedne klase izmijeniti listu
parametara tako da im se predaje objekt klase čije podatke koriste
Nakon izdvajanja klase, obično se traže zavidne metode

2
Nakupine podataka (engl. data clumps)
(FERIT) DOOP PR 64 / 111
Mirisi u kôdu Odbijeno nasljedstvo

Podklase koje ne žele ili ne koriste (sve) atribute i metode


nadklase3 mogu ukazivati na neispravnu hijerarhiju
◦ Ako podklasa koristi samo neke atribute i metode nadklase
◦ ... napraviti novu srodnu podklasu i prebaciti one koje ne koristi
tamo
◦ ovaj miris često nije intenzivan i može proći bez potrebe za
refaktoriranjem ukoliko ne pravi probleme
◦ Ako podklasa ne želi (u potpunosti) podržati sučelje nadklase
◦ ... eliminirati nasljed̄ivanje i delegirati posao na nadklasu
uvod̄enjem nadklase kao atributa i doradom metoda

3
Odbijeno nasljedstvo (engl. refused bequest)
(FERIT) DOOP PR 65 / 111
Mirisi u kôdu Lijene klase

Sve klase trebaju biti vrijedne održavanja4


◦ Vrlo male klase nastale kao posljedica refaktoriranja, klase koje su
uvedene zbog promjena koje nisu dovršene ili podklase slične
nadklasama često su višak kojeg je moguće ukloniti
◦ Ako se radi o podklasi koja je vrlo slična nadklasi
◦ ... prebaciti njena svojstva u nadklasu
◦ Ako se radi o klasi koja nema osobitu funkcionalnost
◦ ... prebaciti njena obilježja u drugu klasu

4
Lijena klasa (engl. lazy class)
(FERIT) DOOP PR 66 / 111
Mirisi u kôdu Privremeni atributi

Atributi koji nisu uvijek korišteni i nemaju uvijek


vrijednosti5 izazivaju nejasnoće i otežavaju razumijevanje
klase
◦ U pravilu, takvi atributi nisu neophodni za klasu i korišteni su samo
unutar neke metode (za potrebe nekih izračuna)
◦ ... može ih se izdvojiti u posebnu klasu uz ostali dio kôda koji se
odnosi na njih, odnosno uz metode koje ih koriste
◦ ... rezultat se naziva metodnim objektom (engl. method object)

5
Privremeni atribut (engl. temporary field)
(FERIT) DOOP PR 67 / 111
Mirisi u kôdu Divergentne promjene

Ukoliko jedna izmjena klase zahtijeva i izmjene njenih


drugih dijelova6 , onda vjerojatno ima nevezanih
funkcionalnosti/odgovornosti
◦ ... Izdvojiti sve dijelove koje nužno promijeniti zbog odgovarajuće
izmjene u posebnu klasu

6
Divergentna promjena (engl. divergent change)
(FERIT) DOOP PR 68 / 111
Mirisi u kôdu Operacije sačmarom

Ako izmjene klase zahtijevaju male izmjene i u nekoliko


drugih klasa7 , održvanje je značajno otežano
◦ ... Izdvojiti sve atribute i metode koje se mijenjaju u jednu od klasa
ili stvoriti novu klasu koja ih objedinjuje
◦ ... može dovesti do divergentnih promjena
Načelno pravilo je sastaviti zajedno stvari koje se zajedno mijenjaju

7
Operacije sačmarom (engl. shotgun surgery)
(FERIT) DOOP PR 69 / 111
Mirisi u kôdu Podatkovne klase

Klase koje sadrže samo atribute i metode za njhovo


dohvaćanje i postavljanje8 su samo spremnici podataka koji
nemaju odgovornost – takve klase su vjerojatno previše
korištene od strane drugih
◦ Ako klasa sadrži atribute koji imaju javni pristup
◦ ... učiniti ih privatnim i uvesti metode za dohvaćanje i postavljanje
◦ Ako metoda vraća kolekciju
◦ ... učiniti da vraća samo pogled na tu kolekciju i dodati metode za
dodavanje i uklanjanje elemenata kolekcije
◦ Ako se neke atribute ne smije mijenjati
◦ ... ukloniti metode za postavljanje

(FERIT) DOOP PR 70 / 111


Mirisi u kôdu Podatkovne klase

◦ Ako metode drugih klasa intenzivno koriste podatke


◦ ... pokušati prebaciti te metode u klasu ili izdvojiti dijelove kao
metode i onda ih prebaciti
◦ Nakon uvod̄enja odgovornosti neki atributi više nisu korišteni
izvana
◦ ... učiniti metode za dohvaćanje i postavljanje tih atributa privatnima

8
Podatkovna klasa (engl. data class)
(FERIT) DOOP PR 71 / 111
Mirisi u kôdu Opežna uporaba komentara

Komentari se često koriste kao dezodorans


◦ Opežna uporaba komentara često upućuje na loš kôd
◦ ... nakon refaktoriranja često postaju nepotrebni/suvišni
◦ Ako se komentar odnosi na dio metode (opisuje što on radi)
◦ ... izdvojiti taj dio kao posebnu metodu
◦ ... ako se radi o malenoj metodi promijeniti joj ime koje ukazuje na
namjeru komentara
Prije pisanje komentara pokušati refaktorirati kôd tako da budu
nepotrebni

(FERIT) DOOP PR 72 / 111


Mirisi u kôdu Neprimjerena bliskost

Kada su dvije klase jako vezane9 potrebno je tu vezu


olabaviti
◦ Ako jedna klasa intenzivno koristi metode ili atribute druge klase i
obratno
◦ ... premjestiti metode ili atribute u onu klasu koja ih koristi
◦ Ako se radi o dvosmjernoj vezi
◦ ... možda ju je moguće promijeniti u jednosmjernu (jednostavnije za
održavati) ukoliko jedna od klasa više nema potrebe za
atributima/metodama druge
◦ Ako klase imaju zajedničkih dijelova (atributa i/ili metoda)
◦ ... izdvojiti ih u posebnu klasu
◦ Neprimjerena bliskost može biti i u slučaju osnovne i izvedene
klase
◦ ... uvesti osnovnu klasu kao atribut u izvedenu i izmijeniti metode
tako da delegiraju na nju te potom ukinuti hijerarhiju (ili dio)
9
Neprimjerena bliskost (engl. inappropriate intimacy)
(FERIT) DOOP PR 73 / 111
Mirisi u kôdu Naredba switch

Naredba switch uobičajeno nije dio objektno


orijenitranog kôda
◦ Uporaba naredbe switch najčešće vodi do dupliciranja kôda
◦ ... nasljed̄ivanje i polimorfizam nude se kao rješenje
◦ U pravilu je moguće zamijeniti tip kojeg se provjerava
odgovarajućim podklasama
◦ ... uvesti podklase za sve vrijednosti tipa
◦ ... učiniti metodu ili metode u nadklasi apstraktnima te ih prepisati u
podklasama
◦ Ako se naredba switch koji koristi u samo jednoj ili nekolicini
metoda te se ne očekuju njihove promijene
◦ ... nije nužno uvoditi polimorfizam
◦ ... zamijeniti parametar kojeg se provjerava posebnim metodama
(za svaku vrijednost, jednu metodu) i time ukloniti naredbu switch
Isto vrijedi za if/else lance

(FERIT) DOOP PR 74 / 111


Mirisi u kôdu Čovjek u sredini

Kada neka klasa obavlja previše delegacije10 ona je samo


posrednik u komunikaciji "klijenta" i klase koja zapravo
obavlja posao
◦ Dodavanje novih mogućnosti/ponašanja u klasu na koju se
delegira traži i dodavanje odgovarajućih metoda i u klasu koja vrši
delegaciju ("poslužitelj")
◦ Moguće je ukloniti svu ili dio delegacije iz "poslužitelja"
◦ ... dodati u "poslužitelja" metodu za dohvaćanje delegata
◦ ... ukloniti metode koje je koristio "klijent" od "poslužitelja" i
zamijeniti ih pozivima na delegatu

(FERIT) DOOP PR 75 / 111


Mirisi u kôdu Čovjek u sredini

◦ Ako je delegacija slabo izražena (nekolicina metoda vrši


delegaciju)
◦ ... prebaciti ih u pozivatelja ("klijenta")
◦ Ako se koriste sve metode delegata
◦ ... zamijeniti delegaciju nasljed̄ivanjem – učiniti klasu koja vrši
delegaciju ("poslužitelj") podklasnom delegata

10
Čovjek u sredini (engl. middle man)
(FERIT) DOOP PR 76 / 111
Mirisi u kôdu Sažetak

Nije sve što se osjeti pokvareno – mirisi u kôdu ukazuju na


potencijalne probleme i sami ne moraju biti problem
◦ Neki mirisi ukazuju na nadutost kôda
◦ ... velike klase, velike metode, veliki broj parametara i nakupine
podataka
◦ ... obično rastu vremenom (stoga, često nisu u tavkom obliku od
početka razvoja)
◦ Neki mirisi ukazuju na zlouporabu objektno orijentirane paradigme
◦ ... privremeni atributi, odbijanje nasljedstva i naredba switch
◦ ... obično su poslijedica neispravne primjene objektno orijentiranog
dizajna

(FERIT) DOOP PR 77 / 111


Mirisi u kôdu Sažetak

◦ Neki mirisi ukazuju na nemogućnost ili otežano provod̄enje


promjena
◦ ... divergentne promjene i operacije sačmarom
◦ ... poslijedica je teško održavanje
◦ Neki mirisi ukazuju na suvišne dijelove
◦ ... dupliciranje kôda, komentari, lijene i podatkovne klase
◦ ... njihovo uklanjanje može učiniti kôd jednostavnijim i razumljivijim
◦ Neki mirisi ukazuju na pretjerane ili suvišne veze
◦ ... zavidne metode, neprimjerena bliskost i čovjek u sredini
◦ ... takve veze je potrebno ublažiti

(FERIT) DOOP PR 78 / 111


Anti-obrasci

(FERIT) DOOP PR 79 / 111


Anti-obrasci

Anti-obrasci (engl. anti-patterns)


Uobičajeni pristup/rješenje za dani problem koji je neučinkovit ili loš te
ima negativne poslijedice

◦ Anti-obrasci su nerijetko poslijedica nedovoljnog znanja ili iskustva


u rješavanju postojećeg problema
◦ mogu biti i posljedica neodgovarajuće primjene oblikovnih obrazaca
(engl. design patterns)
◦ Postoje mnogi anti-obrasci pri razvoju programske podrške
◦ božanska klasa ◦ špageti kôd
◦ mrtav kôd ◦ cut-and-paste programiranje
◦ funkcijska dekompozicija ◦ magični brojevi i stringovi
◦ poltergeist ◦ realni tipovi za novac
◦ zlatni čekić ◦ strah od dodavanja klasa

(FERIT) DOOP PR 80 / 111


Anti-obrasci

◦ Nije uvijek jednostavno razlikovati primjenu anti-obrazaca od loših


navika i ideja
◦ uobičajeno korišteni pristup problemu koji ima više negativnih nego
pozitivnih poslijedica
◦ postoji drugačiji pristup koji je dokazano učinkovit za dani problem

(FERIT) DOOP PR 81 / 111


Anti-obrasci Božanska klasa

Kada jedna klasa ima veliku većinu metoda, a podaci su


podijeljeni na druge klase narušen je objektno orijentirani
dizajn
◦ Božanke klase (engl. God class) ili grude (engl. the blob) u osnovi
predstavljaju proceduralni dizajn, gdje se razdvajaju podaci od
njihove obrade
◦ ... imaju brojne odgovornosti
◦ ... imaju brojne ovisnosti (engl. dependencies)
◦ ... često sadrže nepotrebne funkcionalnosti
◦ Obično navedene klase nastaju vremenom i rastu
◦ ... nerijetko sadrže veliki broj (desetke) nepovezanih atributa i
metoda

(FERIT) DOOP PR 82 / 111


Anti-obrasci Božanska klasa

Božanske klase u pravilu je uvijek moguće pojednostaviti


◦ Odrediti srodne metode (i atribute) i pronaći im drugo i prikladno
mjesto (klasu gdje se uklapaju)
◦ Ukloniti daleke/posredne ili redudantne veze (obično ostvarene
kroz agregaciju)
◦ ... obično je božanska klasa posrednik u vezama drugih klasa

(FERIT) DOOP PR 83 / 111


Anti-obrasci Mrtav kôd

Kôd koji nije dokumentiran i o kojem nitko ništa ne zna, a


vjerojatno i ne služi nikakvoj svrsi
◦ Mrtav kôd (engl. dead code) je zaostao kôd kojeg nitko nije
uklonio ili izmijenio iz straha kako će narušiti ispravnost sustava
◦ ... najčešće je beskoristan
◦ ... često nije jasna njegova veza s funkcionalnosti cjelokupnog
sustava
◦ Na takav kôd upućuju:
◦ ... varijable ili drugi oblik kôda koji nema opravdan razlog za
postojanje
◦ ... složene metode i klase (na očigled važne) koje nemaju jasnu
vezu s radom sustava
◦ ... nekorištene metode ili metode naznačene za zamijenu
◦ Mrtav kôd može imati negativne poslijedice na sustav
◦ ... otežava održavanje, proširenje i testiranje
◦ ... može bespotrebno trošiti resurse i utjecati na preformanse

(FERIT) DOOP PR 84 / 111


Anti-obrasci Mrtav kôd

Mrtav kôd većinom je moguće ukloniti


◦ Važno je dobro proučiti cijeli sustav kao bi se pronašao takav kôd
– bespotreban ili nekorišten
◦ ... uklanjanje može prouzročiti greške ili bugove zbog ovisnosti

(FERIT) DOOP PR 85 / 111


Anti-obrasci Funkcijska dekompozicija

Funkcijska dekompozicija upućuje na prodecuralni dizajn,


gdje je svaka funkcija predstavljena klasom
◦ Na funkcijsku dekompoziciju (engl. functional decomposition)
upućuju:
◦ ... klase s imenima koja priliče funkcijama
◦ ... atributi koji su samo korišteni unutar same klase
◦ ... klase koje imaju samo jednu metodu
◦ ... praktički izostanak objektno orijentirane paradigme – nema
nasljed̄ivanja i polimorfizma

(FERIT) DOOP PR 86 / 111


Anti-obrasci Funkcijska dekompozicija

Ispravak funkcijske dekompozicije je vrlo naporan posao i traži definiranje


novog modela dizajna
◦ Klase koje imaju samo jednu metodu obično je moguće spojiti s
nekom drugom klasom
◦ ... neke klase služe samo kao pomoć drugima
◦ Klase koje imaju samo jednu metodu predstavljaju u pravilu vrlo
finu podijelu posla i obično ih je moguće kombinirati u jednu klasu
◦ ... klase koje rade s istim podacima, ali različite poslove/zadatke

(FERIT) DOOP PR 87 / 111


Anti-obrasci Poltergeist

Ograničene klase koje služe samo pokretanje neke ili nekih


akcija u sustavu
◦ Takve klase obično upućuju na nedostatke u objektno
orijentiranom dizajnu
◦ ... često u imenu sadrže riječ "controller" ili "manager"
◦ ... obično ne sadrže atribute (engl. stateless)
◦ ... obično služe za neki oblik inicijalizacije (pozivaju metode nekih
drugih klasa) i potom nisu više korištene
◦ Slično kao s božanskom klasom, neke ili sve klase
komuniciraju/med̄udjeluju samo preko poltergeist klase

(FERIT) DOOP PR 88 / 111


Anti-obrasci Poltergeist

Poltergeist klase treba ukloniti iz sustava


◦ Nakon uklanjanja može se izgubiti med̄udjelovanje izmed̄u nekih
ili svih klasa
◦ ... to med̄udjelovanje je potrebno ugraditi u postojeću hijerarhiju

(FERIT) DOOP PR 89 / 111


Anti-obrasci Zlatni čekić

Jedan te isti pristup se koristi u potpuno različitim


proizvodima ili dijelovima proizvoda, a za koji ne mora biti
prikladan
◦ Zlatnim čekićem (engl. golden hammer) se može smatrati neka
programska podrška izvana (primjerice, baza podataka i dodatni
alati) ili nekolicina oblikovnih obrazaca
◦ Zlatni čekić se pokušava forsirati u svim razvojima novih proizvoda
◦ ... utječe na arhitekturu sustava (ista arhitekutra se ogleda u
različitim proizvodima)
◦ ... može negativno utjecati na performanse proizvoda
◦ ... zahtjevi iz specifikacije mogu ostati neispunjeni

(FERIT) DOOP PR 90 / 111


Anti-obrasci Špageti kôd

Kôd kojem manjka struktura i stoga je vrlo teško


razumljiv
◦ Na špageti kôd (engl. spaghetti code) upućuje:
◦ ... minimalne veze izmed̄u klasa
◦ ... mnoge metode nemaju parametre i koriste samo atribute klase
kojoj pripadaju
◦ ... ne koristi se nasljed̄ivanje i polimorfizam
◦ ... vrlo teško održavanje i proširenje (jednostavnije je iznova krenuti
s razvojem)

(FERIT) DOOP PR 91 / 111


Anti-obrasci Špageti kôd

Struktura je iznimno važna


◦ Stalno refaktoriranje (čišćenje kôda) igra ključnu ulogu u
održavanju jasne strukture kôda
◦ ... dobro strukturirani kôd ima dug životni vijek i dobru podršku za
proširivanje
◦ Treba biti pažljiv s optimizacijom kôda, jer može narušiti strukturu

Dobro razmisliti i razviti plan razvoja prije početka programiranja

(FERIT) DOOP PR 92 / 111


Anti-obrasci Cut-and-Paste programiranje

Vrlo sličan kôd koji se pojavljuje na više mjesta u sustavu


◦ Dijelovi kôda su vrlo slični, jer nastaju izmijenom/doradom kôda
koji se pokazao da radi u sličnoj situaciji
◦ ... obično vodi do dupliciranja kôda
◦ ... otežava održavanje

(FERIT) DOOP PR 93 / 111


Anti-obrasci Magični brojevi i stringovi

Značenje numeričkih i string konstanti je (dijelomično)


skriveno bez uporabe opisnih imena
◦ Uporaba konstanti bez imena može izazvati nejasnoće i probleme
◦ ... sama vrijednost nekome ne mora ništa značiti
◦ ... otežana pretraga kôda i zamijena vrijednosti
◦ Uglavnom se brojevi 0, 2 i 100 ne smatraju magičnim brojevima

(FERIT) DOOP PR 94 / 111


Anti-obrasci Realni tipovi za novac

Realni tipovi podataka nisu pogodni za izračune kada je


novac u pitanju
◦ Realni tipovi kao float i double mogu samo ispravno predstaviti
racionalne brojeve čiji je nazivnik potencija broja 2
◦ ... za ostale nazivnike koriste se ponavljajući uzorci binarnih
vrijednosti
◦ Iako realni tipovi naočigled pružaju dovoljnu preciznost, male
greške se nakupljaju svakom aritmetičkom operacijom
◦ ... greške u izračunima s novcem nisu prihvatljive

(FERIT) DOOP PR 95 / 111


Anti-obrasci Realni tipovi za novac

Realni tipove podataka treba izbjegavati kada su u pitanju izračuni koji se


odnose na novac
◦ Za mnoge potrebe je dovoljno koristiti cjelobrojne tipove podataka
i sve novčane iznose prikazivati u najmanjoj valutnoj jedinici
◦ Ako je potrebna preciznost i na nižoj razini od najmanje valutne
jedinice
◦ ... koristiti posebne tipove dostupne u jeziku kojeg se koristi ili
izvana preko neke biblioteke

(FERIT) DOOP PR 96 / 111


Anti-obrasci Strah od dodavanja klasa

Dodavanje klasa ne treba zaobilaziti ubacivanjem


funkcionalnosti u postojeće klase
◦ Veći broj klasa najčešće čini dizajn jasnijim i razumljivijim, a ne
obratno
◦ ... može pridonijeti i labavim vezama izmed̄u klasa
◦ Razdvajanje velikih klasa na nekoliko manjih može olakšati
održavanje i razumijevanje
◦ ... pridonosi održavanju SRP
◦ Slično vrijedi i za metode

(FERIT) DOOP PR 97 / 111


Anti-obrasci Sažetak

Anti-obrasci uglavnom ukazuju na nedovoljno znanje ili iskustvo u


rješavanja danog problema
◦ Kao mirisi u kôdu, anti-obrasci upućuju na loš pristup razvoju
programske podrške
◦ Anti-obrasci u kôdu mogu imati teške posljedice
◦ ... nejasna ili nepostojeća struktura kôda
◦ ... vrlo teško ili nemoguće održavanje i proširivanje
◦ ... neispunjavanje svih korisničkih zahtjeva
◦ ... negativan učinak na performanse

(FERIT) DOOP PR 98 / 111


S.T.U.P.I.D. kôd

(FERIT) DOOP PR 99 / 111


S.T.U.P.I.D.

S.T.U.P.I.D. kôd
Šest čestih pristupa pisanju kôda koji ga čine teškim za održavanje,
proširivanje i testiranje

◦ Singleton
◦ Tight coupling
◦ Untesability
◦ Premature opitmisation
◦ Indescriptive naming
◦ Duplication

(FERIT) DOOP PR 100 / 111


S.T.U.P.I.D. Singleton

Singleton je oblikovni obrazac koje se često smatra


anti-obrascem
◦ Singleton kao oblikovni obrazac služi za ograničavanje broja
instanci klase na jedan objekt
◦ ... referencu na objekt je moguće dobiti praktički bilo gdje – slično
kao globalnoj varijabli
◦ ... klasa je sama odgovorna za održavanje jedne instance te njeno
stvaranje
◦ ... klasa rukuje na koji način i kada "klijenti" mogu pristupiti instanci
◦ Postoje situacije gdje je zapravo potrebna samo jedna instanca
klase
◦ ... primjerice, klase koje služe za rukovanje različitim ured̄ajima
◦ ... primjerice, klasa koja služi za logiranje
◦ ... primjerice, klasa koja predstavlja bazen niti

(FERIT) DOOP PR 101 / 111


S.T.U.P.I.D. Singleton

Nacrt Singleton oblikovnog obrasca


1 class Singleton {
2 private static Singleton instance ; // jedinstvena instanca
3 private Singleton () { } // privatni konstruktor
4
5 // jedini nacin pristupa instanci
6 // lijeno instanciranje (engl. lazy instantiation )
7 public static Singleton GetInstance () {
8 if ( instance == null) {
9 instance = new Singleton ();
10 }
11 return instance ;
12 }
13 }

(FERIT) DOOP PR 102 / 111


S.T.U.P.I.D. Singleton

◦ Singleton oblikovni obrazac je iznimno popularan, ali njegova


uporaba ima posljedice
◦ ... uvodi globalno stanje u program – vrlo teško za testiranje
◦ ... vrlo često je korišten u kôdu – dovodi do skrivanja ovisnosti
◦ Singleton je često korišten na mjestima gdje nije potreban

(FERIT) DOOP PR 103 / 111


S.T.U.P.I.D. Tight coupling

Usko povezane klase su teške za izmijeniti, testirati i


ponovno upotrijebiti
◦ Dvije klase su povezane ako je potrebno promijeniti drugu klasu
zbog promijene prve
◦ ... primjerice, instanciranje objekta jedne klase u nekoj drugoj klasi
◦ ... primjerice, jedna klasa poziva metodu ili koristi vrijednosti
atributa druge klase
◦ Povezivanje klasa je nužno, ali je potrebno rukovati razinom
povezivanja
◦ ... težiti labavim vezama
◦ ... implementacija ovisi o sučelju, ali ne i obratno
◦ ... usko povezane klase je teško testirati i ponovno iskoristiti

(FERIT) DOOP PR 104 / 111


S.T.U.P.I.D. Untestability

Testiranje klasa, odnosno njenih metoda trebalo bi biti


jednostavno
◦ Nemogućnost testiranja ili otežano testiranje najčešće je
posljedica usko povezanih klasa
◦ ... potrebno je izdvojiti klasu koju se želi testirati, što je teško ako je
ovisna o jednoj ili više drugih klasa

(FERIT) DOOP PR 105 / 111


S.T.U.P.I.D. Premature optimisation

Optimizacija kôda za vrijeme njegovog pisanja obično vodi


prema narušavanju strukture te kôdu koji je nejasan i težak
za održavanje
◦ Preporuka je optimirati kôd tek nakon što se ustanovi da je
optimizacija potrebna
◦ ... vrednovati performanse kôda nakon što je završen i onda
pristupiti potrebnoj otimizaciji
◦ ... optimiranje unaprijed krši YAGNI načelo
◦ Prije otimizacije je potrebno pronaći vremenski kritične dijelove te
samo njih optimirati
◦ ... uporabom profilera ustanoviti koji dijelovi troše najviše
resursa/vremena
◦ Čist kôd nerijetko je brži i troši manje resura u odnosu na kôd koji
to nije
◦ Čist kôd je mnogo jednostavnije optimirati u odnosu na nečist kôd

(FERIT) DOOP PR 106 / 111


S.T.U.P.I.D. Indescriptive naming

Imena su svugdje u kôdu i trebaju ukazivati na namjeru


◦ Imena trebaju ukazivati na to što nešto predstavlja i kako se koristi
◦ ... ne koristiti akronime, skraćenice i slično
◦ ... izbjegavati kodiranje tipova podataka i drugih informacija u imena
◦ ... komentari ne trebaju biti potrebni za objašnjenje imena
◦ Odabir opisnih imena je prvi korak prema čistom kôdu
◦ ... koristiti imena koja je moguće izgovoriti
◦ ... koristiti imenice za u imenima klasa te glagole u imenima metoda

(FERIT) DOOP PR 107 / 111


S.T.U.P.I.D. Duplication

Dupliciranje kôda uvelike otežava održavanje i proširivanje


◦ Dupliciranje kôda je protiv DRY načela
◦ ... svaki oblik ponašanja ili funkcionalnosti treba se pojavljivati samo
jednom
◦ Duplicirani kôd koje je obavlja isti zadatak, ali na različite način je
teško pronaći
◦ Ujedinjavanje dupliciranog kôda skraćuje tekst programa i
pojednostavljuje ga

(FERIT) DOOP PR 108 / 111


Zaključak

(FERIT) DOOP PR 109 / 111


Zaključak

◦ Uporaba objektno orijentiranih načela u razvoju vodi prema


programskoj podršci koju je jednostavno održavati i proširivati
◦ Zanemarivanje postojećeg kôda može brzo i lako dovesti do
nastanka mirisa u kôdu
◦ Nedostatak znanja ili iskustva može dovesti do primjene
kontraproduktivnih rješenja za probleme koje se susreću pri
razvoju

Stalno refaktoriranje postojećeg kôda je ključno u održavanju (ili


postizanju) čistog kôda
◦ Preporuka je raditi izmjene kôda u malim koracima kako bi se
lakše uočile eventualno uvedene pogreške

(FERIT) DOOP PR 110 / 111


Za kraj

"Any fool can write code that a computer can understand. Good
programmers write code that humans can understand."
Martin Fowler

(FERIT) DOOP PR 111 / 111

You might also like