Önceki Parçalar:
Bir sınıfın değişmek için tek bir nedeni olmalıdır
// Bad class UserSettingsService { constructor(user: IUser) { this.user = user; } changeSettings(settings: IUserSettings): void { if (this.isUserValidated()) { // ... } } getUserInfo(): Promise<IUserSettings> { // ... } async isUserValidated(): Promise<boolean> { const userInfo = await this.getUserInfo(); // ... } }
Bu örnekte sınıfımız farklı yönlerde eylemler gerçekleştiriyor: bağlamı kuruyor, değiştiriyor ve doğruluyor.
// Better class UserAuth { constructor(user: IUser) { this.user = user; } getUserInfo(): Promise<IUserSettings> { // ... } async isUserValidated(): boolean { const userInfo = await this.getUserInfo(); // ... } } class UserSettings { constructor(user: IUser) { this.user = user; this.auth = new UserAuth(user); } changeSettings(settings: IUserSettings): void { if (this.auth.isUserValidated()) { // ... } } }
Yazılım varlıkları (sınıflar, modüller, işlevler) genişletmeye açık, değişiklik yapmaya kapalı olmalıdır
// Bad class Product { id: number; name: string[]; price: number; protected constructor(id: number, name: string[], price: number) { this.id = id; this.name = name; this.price = price; } } class Ananas extends Product { constructor(id: number, name: string[], price: number) { super(id, name, price); } } class Banana extends Product { constructor(id: number, name: string[], price: string) { super(id, name, price); } } class HttpRequestCost { constructor(product: Product) { this.product = product; } getDeliveryCost(): number { if (product instanceOf Ananas) { return requestAnanas(url).then(...); } if (product instanceOf Banana) { return requestBanana(url).then(...); } } } function requestAnanas(url: string): Promise<ICost> { // logic for ananas } function requestBanana(url: string): Promise<ICost> { // logic for bananas }
Bu örnekte sorun HttpRequestCost
sınıfındadır, yöntemde getDeliveryCost
farklı ürün türlerinin hesaplanmasına yönelik koşullar içerir ve her ürün türü için ayrı yöntemler kullanırız. Dolayısıyla, yeni bir ürün türü eklememiz gerekiyorsa HttpRequestCost
sınıfını değiştirmeliyiz ve bu güvenli değildir; beklenmedik sonuçlar elde edebiliriz.
Bunu önlemek için, Product
sınıfında gerçekleştirmeler olmadan soyut bir yöntem request
oluşturmalıyız. Özel farkındalık şu sınıfları miras alacaktır: Ananas ve Muz. İsteği kendileri gerçekleştirecekler.
HttpRequestCost
Product
sınıfı arayüzünü takip ederek product
parametresini alacak ve HttpRequestCost
belirli bağımlılıkları aktardığımızda zaten kendisi için request
yöntemini gerçekleştirecektir.
// Better abstract class Product { id: number; name: string[]; price: string; constructor(id: number, name: string[], price: string) { this.id = id; this.name = name; this.price = price; } abstract request(url: string): void; } class Ananas extends Product { constructor(id: number, name: string[], price: string) { super(id, name, price); } request(url: string): void { // logic for ananas } } class Banana extends Product { constructor(id: number, name: string[], price: string) { super(id, name, price); } request(url: string): void { // logic for bananas } } class HttpRequestCost { constructor(product: Product) { this.product = product; } request(): Promise<void> { return this.product.request(url).then(...); } }
Bir üst sınıfın nesneleri, uygulamayı bozmadan alt sınıflarının nesneleri ile değiştirilebilir olmalıdır.
// Bad class Worker { work(): void {/../} access(): void { console.log('Have an access to closed perimeter'); } } class Programmer extends Worker { createDatabase(): void {/../} } class Seller extends Worker { sale(): void {/../} } class Designer extends Worker { access(): void { throwError('No access'); } }
Bu örnekte Contractor
sınıfıyla ilgili bir sorunumuz var. Designer
, Programmer
ve Seller
tümü Workers'tır ve Worker
üst sınıfından miras alırlar. Ancak aynı zamanda Tasarımcılar Çalışan değil Yüklenici oldukları için kapalı çevreye erişimleri yoktur. access
yöntemini geçersiz kıldık ve Liskov Değiştirme İlkesini çiğnedik.
Bu prensip bize, Worker
üst sınıfını alt sınıfıyla (örneğin Designer
sınıfı) değiştirirsek işlevselliğin bozulmaması gerektiğini söyler. Ancak bunu yaparsak, Programmer
sınıfının işlevselliği bozulacaktır; access
yöntemi, Designer
sınıfından beklenmeyen gerçekleşmelere sahip olacaktır.
// Better class Worker { work(): void {/../} } class Employee extends Worker { access(): void { console.log('Have an access to closed perimeter'); } } class Contractor extends Worker { addNewContract(): void {/../} } class Programmer extends Employee { createDatabase(): void {/../} } class Saler extends Employee { sale(): void {/../} } class Designer extends Contractor { makeDesign(): void {/../} }
Employee
ve Contractor
için yeni soyutlama katmanları oluşturduk ve access
yöntemini Employee
sınıfına taşıdık ve belirli gerçekleştirmeyi tanımladık. Worker
sınıfını Contractor
alt sınıfıyla değiştirirsek Worker
işlevselliği bozulmayacaktır.