Skip to content

Latest commit

 

History

History
376 lines (289 loc) · 11 KB

01_03_Отношения_между_классами.md

File metadata and controls

376 lines (289 loc) · 11 KB

Отношения между классами

  1. Наседование
  2. Реализация
  3. Ассоциация
  4. Агрегация
  5. Композиция

Наследование

Наследование это одна из парадигм Объектно-ориентированного программирования. Наследование позволяет одному классу унаследовать поведение другого класса.

Класс от которого наследуются называют — родительским. Класс который наследуется от другого класса, называют — дочерним.

class Parent {
    public void SayHi() {
        Console.WriteLine("Parent:: Hi!");
    }
}

class Child : Parent { }

class Programs {
    static void Main(string[] args) {
        Child child = new Child();
        // Дочерний класс использует метод родительского
        child.SayHi(); 
        
        // Вывод на консоль: 
        // Parent:: Hi!
    }
}

Дочерний класс может использовать все поля и методы своего родителя помеченные как открытые (public) или защищенные (protected) .

Наследование является отношением типа — is a; то есть один класс является подклассом другого. Примеры: машина является транспортом, катер является транспортом, меч является холодным оружием, холодное оружие является подвидом оружия…

class Weapon {
    private double damage;
    public Weapon (double damage) {
        this.damage = damage;
    } 
    public double GetDamage() {
        return damage;
    }
}

class ColdSteel : Weapon {}
class Sword : ColdSteel {}
class Knife : ColdSteel {}

class Guns : Weapon {}
class Pistol : Guns {}
class Rifle : Guns {}
classDiagram
    Weapon <|-- ColdSteel
    ColdSteel <|-- Sword
    ColdSteel <|-- Knife

    Weapon <|-- Guns
    Guns <|-- Pistol
    Guns <|-- Rifle

    class Weapon {
        -damage
        +GetDamage()
    } 
Loading

Наследование используется для создания иерархии похожих по поведению классов.

Реализация

Реализация подразумевает наследование от интерфейса и реализация всех его методов.

Интерфейс — это контракт с классом, который обязует класс реализовать все методы интерфейса.

interface IWeapon {
    public double GetDamage ();
}

class Guns : IWeapon {
    private double damage;
    
    public Guns (double damage) {
        this.damage = damage;
    }
    
    // Реализация интерфейса
    public double GetDamage () {
        Console.WriteLine("Guns:: " + damage);
        return damage;
    }
}
classDiagram
IWeapon <.. Guns

class IWeapon {
	<<interface>>
	GetDamage()
}

class Guns {
	-damage
	+GetDamage()
}
Loading

Интерфейс необходимо использовать, когда нужно создать контракт с разными классами на реализацию определенных методов или свойств.

Ассоциация

Ассоциация это отношение в котором объекты ссылаются друг на друга, но остаются независимыми. То есть в одном объекте есть ссылка на другой, которую можно передать через метод или напрямую.

Отношение может быть один к одному (1..1) (или бинарным):

class Sword {} 
class Slayer {
    private Sword sword;
    
    public void ChangeSword (Sword sword) {
        this.sword = sword;
    }
}

К примеру, персонаж игрока в компьютерной игре может существовать отдельно от его оружия, как и наоборот, — персонаж может просто выкинуть свой меч или отдать его другому игроку. То есть объекты могут существовать отдельно друг от друга. Однако в данном примере персонаж может держать только один меч.

classDiagram
Slayer --> Sword

class Slayer {
	-sword : Sword 
	+ChangeSword() : void
}
Loading

Отношение может быть один ко многим (1..М) (или N-нарным):

class Gem {}
class Sword {
    private List<Gem> gems;
    
    public void AddGem (Gem gem) {
        gems.Add (gem);
    }
    
    public void RemoveGem (Gem gem) {
        gems.Remove (gem);
    }
} 

К примеру, в меч можно вставлять магические камни, которые будут давать дополнительные эффекты для оружия. Внутри класса содержится структура данных, которая хранит множество магических камней. Магические камни могут существовать отдельно от меча.

classDiagram
Sword --> Gem

class Sword {
	-List~Gem~ gems
	+AddGem(Gem gem) : void
	+RemoveGem(Gem gem) : void
}
Loading

Агрегация

При реализации объект находится внутри другого объекта, но они не зависят друг от друга и могут существовать порознь, в отличии от композиции, где внутренний объект полностью зависит от главного.

При агрегации объект создается извне и передается через конструктор.

Агрегация пример слабой связи, так как жизненный цикл вложенного объекта не зависит от жизненного цикла объекта контейнера. Если главный объект будет уничтожен, вложенный объект может продолжить существовать в памяти, если в этом есть необходимость, или быть уничтоженным.

Агрегация является отношением типа — has a.

class Sword {}
class Slayer {
    private Sword sword;
    public Slayer (Sword sword) {
        this.sword = sword;
    }
}

Чуть более интересный пример:

class Sword {
	private double damage;
    
	public Sword (double damage) {
		this.damage = damage;
	}
    
	public double GetDamage () {
		return damage;
	}
    
	protected void SetDamage (double damage) {
		this.damage = damage;
	}
}

class Slayer {
	private Sword sword;
    
	public Slayer (Sword sword) {
		this.sword = sword;
	}
    
	public double GetDamage () {
		Console.WriteLine ("Slayer damage: " + sword.GetDamage ());
		return sword.GetDamage ();
	}
}

class Program {
	static void Main (string[] args) {
		Sword sword = new Sword (100);
		Slayer slayer = new Slayer (sword);
		slayer.GetDamage ();
        
        // Вывод на консоль:
        // Slayer damage: 100
	}
}
classDiagram
Slayer o-- Sword

class Sword {
	-damage : double
	+GetDamage() : double
	-SetDamage(double damage) : void
}

class Slayer {
	-sword : Sword
	+GetDamage() : double
}
Loading

Как правило при агрегации определяется ссылка не на конкретный класс, а на интерфейс или абстрактный класс.

abstract class Sword {}
class OrcSword : Sword {}
class Slayer {
    private Sword sword;
    public Slayer (Sword sword) {
        this.sword = sword;
    }
}

Sword sword = new OrcSword ();
Slayer slayer = new Slayer (sword);

Примером агрегации может служить паттерн “Декоратор” или “Адаптер”.

Композиция

При композиции один объект находится внутри другого и полностью зависит от его жизненного цикла, в отличии от агрегации, где объекты могут существовать порознь.

При композиции внутренние объекты создаются в конструкторе класса при его инициализации. Если главный объект, который содержит в себе другие будет уничтожен, будут уничтожены все внутренние объекты.

Композиция пример сильной связи, так как жизнь внутреннего объекта напрямую зависит от главного, который его содержит.

Композиция является отношением типа — has a.

class Heart {}
class Player {
    private Heart heart;
    public Player () {
        heart = new Heart ();
    }
}

Чуть более интересный пример:

class Heart {
	private double hitPoints;
	public event EventHandler OnDie;
    
	public Heart (double hitPoints) {
		this.hitPoints = hitPoints;
	}
    
	public void Heal (double points) {
		hitPoints += points;
	}
    
	public void Hurt (double points) {
		hitPoints -= points;
		if (hitPoints <= 0) {
			hitPoints = 0;
			Console.WriteLine("Heart Died!");
			OnDie?.Invoke (this, EventArgs.Empty);
		}
	}
}

class Player {
	private Heart heart;
    
	public Player (double hitPoints) {
		heart = new Heart (hitPoints);
		heart.OnDie += Die;
	}
    
	public void Heal (double healPoints) {
		heart.Heal (healPoints);
	}
    
	public void Hurt (double damage) {
		heart.Hurt (damage);
	}
    
	protected void Die (object sender, EventArgs e) {
		Console.WriteLine("Player Died!");
		// Do something...
	}
}

class Program {
	static void Main (string[] args) {
		Player player = new Player(100);
		player.Hurt(50);
		player.Hurt(25);
		player.Hurt(25);
        
        // Вывод на консоль:
		// Heart Died!
		// Player Died!
	}
}
classDiagram
Player *-- Heart

class Heart {
	-hitPoints : double
	-OnDie : event
	+Heal(double points) : void
	+Hurt(double points) : void
}

class Player {
	-heart : Heart
	+Heal(double points) : void
	+Hurt(double damage) : void
	-Die(object sender, EventHandler e) : void
}
Loading