Search
moon
sun
๐Ÿ“

LSP : The Liskov Substitution Principle

Category
as S/W ์—”์ง€๋‹ˆ์–ด
Tags
SOLID
Architecture
S/W Engineering
Study Note
LSP
Created time
2009/04/15

Definition

โ€ข
์ƒ์† ์‹œ ๋ถ€๋ชจ์™€ ์ž์‹์˜ ๊ด€๊ณ„๊ฐ€ ๋ฐ˜๋“œ์‹œ IS-A ๊ด€๊ณ„๋ฅผ ์œ ์ง€ํ•˜๋„๋ก, ์ข€๋” ์ •ํ™•ํžˆ ํ•˜์ž๋ฉด, ๋ถ€๋ชจ๋ฅผ ์น˜ํ™˜(substitution) ๊ฐ€๋Šฅํ•œ ์ž์‹์„ ๋งŒ๋“ค๋„๋ก.
โ€ข
์ƒ์† ๊ด€๊ณ„ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ์˜ ์›์น™. Base type์˜ ์‚ฌ์šฉ ์กฐ๊ฑด์„ derived type์—์„œ ๋ชจ๋‘ ๋”ฐ๋ฅผ ์ˆ˜ ์žˆ๋„๋ก

LSP violated sample

public class Line { public Line(Point p1, Point p2){ /*code*/} } public Point P1 { get { /*code*/} } } public Point P2 { get { /*code*/} } } public virtual bool IsOn(Point p) {/*code*/} } public class LineSegment : Line { public LineSegment(Point p1, Point p2) : base(p1, p2) {} public double Length() { get {/*code*/} } //Line.IsOn()์˜ ์ฐธ์ด ์•„๋ž˜์—์„œ๋Š” ๊ฑฐ์ง“์ด ๋  ์ˆ˜ ์žˆ์Œ. public override bool IsOn(Point p) {/*code*/} } class LineClient { void DoSomething(Line) { /*code*/ } }
C#
๋ณต์‚ฌ
โ€ข
LineClient.DoSomething()์€ ํ•ด๋‹น ๋งค๊ฐœ๋ณ€์ˆ˜(line) ์œ„์˜ ๋ชจ๋“  ์ ์—์„œ IsOn() == true๋ฅผ ๊ธฐ๋Œ€ํ•˜๊ณ  ์ฝ”๋“œ ์ž‘์„ฑ ๊ฐ€๋Šฅ.
โ—ฆ
ํ•˜์ง€๋งŒ ์„ ๋ถ„์ธ LineSegment instance๋ฅผ LineClient.DoSomething()์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋„˜๊ธธ ๊ฒฝ์šฐ, IsOn() == false๊ฐ€ ๊ฐ€๋Šฅํ•จ์œผ๋กœ LineClient.DoSomething()์€ ์œ ํšจํ•˜์ง€ ์•Š์€ ์ฝ”๋“œ๊ฐ€ ๋จ.
โ€ข
Base class client ์ฝ”๋“œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ derived class์— ๋Œ€ํ•œ ์ง€์‹์„ ๊ณ ๋ คํ•˜์ง€ ์•Š๊ณ (base class์— ๋Œ€ํ•œ ์ง€์‹๋งŒ์œผ๋กœ) client ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ. ํ•˜์ง€๋งŒ ํ•ด๋‹น client code๋กœ derived class์˜ instance๊ฐ€ ์ „๋‹ฌ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด ๋•Œ LSP ์œ„๋ฐ˜ ์‹œ ํ•ด๋‹น operation์€ ๋ฌดํšจํ™” ๋จ. ์ฆ‰, ์ž ์žฌ์  ๋ฒ„๊ทธ ์œ ๋ฐœ ๊ฐ€๋Šฅ.

LSP ์ค€์ˆ˜ ๋ฐฉ๋ฒ•

1.
DBC(Design by Contract; ๊ณ„์•ฝ์— ์˜ํ•œ ์„ค๊ณ„)
โ€ข
Overriding ์‹œ, base class์—์„œ์˜ pre condition๊ณผ ๊ฐ™๊ฑฐ๋‚˜ ๋” ์•ฝํ•œ ์ˆ˜์ค€์—์„œ ๋™์ž‘ํ•˜๊ณ , post condition์—์„œ๋Š” ๊ฐ™๊ฑฐ๋‚˜ ๋” ๊ฐ•ํ•œ ์ˆ˜์ค€์—์„œ ๋™์ž‘ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜์—ฌ์•ผ(Base class์˜ ๋ชจ๋“  ๋ช…์‹œ์ /์•”์‹œ์  ๊ณ„์•ฝ์„ ๋งŒ์กฑํ•œ derived class ์ž‘์„ฑ).
2.
deriving ๋Œ€์‹  factoring(๊ณตํ†ต ์ธ์ž ์ถ”์ถœ)
public abstract class LinearObject { public LinearObject(Point p1, Point p2) { /*code*/} } public Point P1 { get { /*code*/} } } public Point P2 { get { /*code*/} } } public abstract virtual bool IsOn(Point p); } public class Line : LinearObject { public Line(Point p1, Point p2) : base(p1, p2) {} public override bool IsOn(Point p) {/*code*/} } public class LineSegment : LinearObject { public LineSegment(Point p1, Point p2) : base(p1, p2) {} public double GetLength() {/*code*/} public override bool IsOn(Point p) {/*code*/} } class LineClient { void DoSomething(Line) { /*code*/ } }
C#
๋ณต์‚ฌ
โ€ข
Line๊ณผ LineSegment์˜ ๊ณตํ†ต์  ํŠน์„ฑ์„ ์ถ”์ถœํ•˜์—ฌ ๊ณตํ†ต ์ƒ์œ„ ํด๋ž˜์Šค(LinearObject) ์ƒ์„ฑ
โ—ฆ
LineClient.DoSomething()์€ ๋” ์ด์ƒ ์ž ์žฌ์  ๋ฒ„๊ทธ๋ฅผ ๊ฐ–์ง€ ์•Š์Œ
์ผ๋ฐ˜์ ์œผ๋กœ factoring์„ ํ†ตํ•œ ๊ณตํ†ต์˜ ์ƒ์œ„ ํด๋ž˜์Šค๋Š” abstract๊ฐ€ ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๊ณ , ํ•ด๋‹น ์ƒ์œ„ ํด๋ž˜์Šค๋Š” ์–ธ์  ๊ฐ€ ๊ทธ ์ž์ฒด๋งŒ์˜ client code๋ฅผ ๊ฐ–๊ธฐ ๋งˆ๋ จ.

Conclusion

โ€ข
LSP ์œ„๋ฐ˜์€ base class ๋งŒ ์•Œ๋˜ client code์˜ ๋ณ€๊ฒฝ์„ ์œ ๋ฐœํ•˜๋ฏ€๋กœ(derived class์— ๋Œ€ํ•œ ๊ณ ๋ ค๊ฐ€ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์—), OCP ์œ„๋ฐ˜์ด๋ž€ ๋ถ€์ •์  ํŒŒ๊ธ‰ ํ˜„์ƒ์„ ์•ผ๊ธฐํ•จ.
โ€ข
OCP ์ค€์ˆ˜, ์ฆ‰ ์ถ”์ƒํ™”/๋‹คํ˜•์„ฑ ๊ธฐ๋ฐ˜์˜ ์˜ฌ๋ฐ”๋ฅธ ์„ค๊ณ„๋ฅผ ์œ„ํ•œ ์ฃผ์š” ์›์น™์„ ์ œ์‹œ