예제: GeometricShapesGeometricShapes 샘플 응용 프로그램에서는 ActionScript 3.0을 사용하여 얼마나 많은 객체 지향 개념과 기능을 적용할 수 있는지 보여 줍니다.
이 예제에는 클래스 인스턴스를 생성하는 "팩토리 메서드"가 나와 있으며 반환값을 인터페이스의 인스턴스로 선언하고 반환된 객체를 일반적인 방식으로 사용하는 방법도 보여 줍니다. 이 샘플에 대한 응용 프로그램 파일을 가져오려면 www.adobe.com/go/learn_programmingAS3samples_flash_kr를 참조하십시오. GeometricShapes 응용 프로그램 파일은 Samples/GeometricShapes 폴더에서 찾을 수 있습니다. 이 응용 프로그램은 다음과 같은 파일로 구성됩니다.
GeometricShapes 클래스 정의GeometricShapes 응용 프로그램에서는 사용자가 기하학적 형태의 유형 및 크기를 지정하도록 합니다. 그런 다음 사용자에게 형태에 대해 설명하고 면적 및 둘레를 알려 줍니다. 이 응용 프로그램의 사용자 인터페이스는 간단하게 구성되어 있습니다. 형태의 유형을 선택하고 크기를 설정하고 설명을 표시하는 데 사용하는 컨트롤 몇 개로 구성됩니다. 이 응용 프로그램에서 가장 흥미로운 부분은 표면에 드러나지 않는 클래스 및 인터페이스의 구조에 있습니다. 이 응용 프로그램에서는 기하학적 형태를 다루지만 그래픽으로 표시하지는 않습니다. 이 예제에서 기하학적 형태를 정의하는 클래스 및 인터페이스는 UML(Unified Modeling Language)을 사용하여 다음 다이어그램과 같이 나타낼 수 있습니다. GeometricShapes 예제 클래스 인터페이스를 사용하여 공통 비헤이비어 정의이 GeometricShapes 응용 프로그램에서는 세 가지 유형의 형태, 즉 원, 사각형 및 등변 삼각형을 다룹니다. GeometricShapes의 클래스 구조는 매우 작은 인터페이스인 IGeometricShape에서 시작됩니다. 이 인터페이스에는 세 가지 유형의 형태 모두에 공통적인 메서드가 나열되어 있습니다. package com.example.programmingas3.geometricshapes { public interface IGeometricShape { function getArea():Number; function describe():String; } } 이 인터페이스에는 두 개의 메서드가 정의되어 있습니다. getArea() 메서드는 형태의 면적을 계산하여 반환하며 describe() 메서드는 문자열을 결합하여 형태의 속성을 설명하는 텍스트를 만듭니다. 각 모양의 둘레도 확인하고 싶은 경우를 가정해 봅니다. 원의 둘레는 원주라고 하며 고유한 계산 방법이 있으므로 비헤이비어가 삼각형 또는 사각형과 달라지게 됩니다. 그러나 삼각형, 사각형 및 기타 다각형 간에는 충분한 유사성이 있으므로 이들을 위한 새로운 클래스 인터페이스, 즉 IPolygon을 정의하는 것은 의미가 있습니다. 또한 IPolygon 인터페이스는 여기에 나와 있듯이 상당히 간단하게 정의됩니다. package com.example.programmingas3.geometricshapes { public interface IPolygon extends IGeometricShape { function getPerimeter():Number; function getSumOfAngles():Number; } } 이 인터페이스에는 모든 다각형에 공통적인 두 개의 메서드가 정의되어 있습니다. getPerimeter() 메서드는 모든 변의 길이를 합산하여 측정하며 getSumOfAngles() 메서드는 내부 각도를 모두 더합니다. IPolygon 인터페이스는 IGeometricShape 인터페이스를 확장하므로 IPolygon 인터페이스를 구현하는 모든 클래스에서는 IGeometricShape 인터페이스와 IPolygon 인터페이스에 두 개씩 정의되어 있는 네 개의 메서드를 모두 선언해야 합니다. 형태 클래스 정의형태 유형별로 공통적인 메서드가 발견되면 각각의 형태 클래스를 정의할 수 있습니다. 구현해야 하는 메서드의 수를 기준으로 할 때 가장 간단한 형태는 다음에 나와 있듯이 Circle 클래스입니다. package com.example.programmingas3.geometricshapes { public class Circle implements IGeometricShape { public var diameter:Number; public function Circle(diam:Number = 100):void { this.diameter = diam; } public function getArea():Number { // The formula is Pi * radius * radius. var radius:Number = diameter / 2; return Math.PI * radius * radius; } public function getCircumference():Number { // The formula is Pi * diameter. return Math.PI * diameter; } public function describe():String { var desc:String = "This shape is a Circle.\n"; desc += "Its diameter is " + diameter + " pixels.\n"; desc += "Its area is " + getArea() + ".\n"; desc += "Its circumference is " + getCircumference() + ".\n"; return desc; } } } Circle 클래스는 IGeometricShape 인터페이스를 구현하므로 getArea() 메서드 및 describe() 메서드에 대한 코드가 작성되어야 합니다. 또한 이 클래스에는 getCircumference()라는 고유한 메서드가 정의되어 있습니다. Circle 클래스에는 다른 다각형 클래스에서는 찾을 수 없는 diameter라는 속성도 선언되어 있습니다. 다른 두 유형의 형태, 즉 정사각형과 등변 삼각형은 몇 가지 추가적인 공통점이 있습니다. 예를 들어 두 형태 모두 변의 길이가 동일하며 일반적인 공식을 사용하여 둘레 및 내부 각도의 합을 계산할 수 있습니다. 사실 이러한 일반적인 공식은 앞으로 정의할 다른 모든 일반 다각형에도 적용됩니다. RegularPolygon 클래스는 이후에 Square 클래스 및 EquilateralTriangle 클래스의 수퍼 클래스가 됩니다. 수퍼 클래스를 사용하면 한 곳에 공통적인 메서드를 정의할 수 있으므로 이러한 메서드를 각 하위 클래스에서 별도로 정의하지 않아도 됩니다. RegularPolygon 클래스의 코드가 다음에 나와 있습니다. package com.example.programmingas3.geometricshapes { public class RegularPolygon implements IPolygon { public var numSides:int; public var sideLength:Number; public function RegularPolygon(len:Number = 100, sides:int = 3):void { this.sideLength = len; this.numSides = sides; } public function getArea():Number { // This method should be overridden in subclasses. return 0; } public function getPerimeter():Number { return sideLength * numSides; } public function getSumOfAngles():Number { if (numSides >= 3) { return ((numSides - 2) * 180); } else { return 0; } } public function describe():String { var desc:String = "Each side is " + sideLength + " pixels long.\n"; desc += "Its area is " + getArea() + " pixels square.\n"; desc += "Its perimeter is " + getPerimeter() + " pixels long.\n"; desc += "The sum of all interior angles in this shape is " + getSumOfAngles() + " degrees.\n"; return desc; } } } RegularPolygon 클래스에서는 먼저 모든 일반 다각형에 공통적인 두 가지 속성, 즉 sideLength 속성(각 변의 길이) 및 numSides 속성(변의 수)을 선언합니다. RegularPolygon 클래스에서는 IPolygon 인터페이스를 구현하며 이 인터페이스의 메서드 네 개를 모두 선언합니다. 이러한 메서드 중 두 개, 즉 getPerimeter() 및 getSumOfAngles() 메서드는 일반적인 공식을 사용하여 구현합니다. getArea() 메서드에 사용되는 공식은 형태에 따라 다르므로 이 메서드의 기본 클래스 버전에는 하위 클래스 메서드에 상속될 수 있는 공통적인 논리를 포함할 수 없습니다. 대신 간단히 기본값 0을 반환하여 면적이 계산되지 않았음을 나타냅니다. 각 형태의 면적을 올바르게 계산하려면 RegularPolygon 클래스의 하위 클래스에서 getArea() 메서드를 재정의해야 합니다. EquilateralTriangle 클래스의 다음 코드에서는 getArea() 메서드를 재정의하는 방법을 보여 줍니다. package com.example.programmingas3.geometricshapes { public class EquilateralTriangle extends RegularPolygon { public function EquilateralTriangle(len:Number = 100):void { super(len, 3); } public override function getArea():Number { // The formula is ((sideLength squared) * (square root of 3)) / 4. return ( (this.sideLength * this.sideLength) * Math.sqrt(3) ) / 4; } public override function describe():String { /* starts with the name of the shape, then delegates the rest of the description work to the RegularPolygon superclass */ var desc:String = "This shape is an equilateral Triangle.\n"; desc += super.describe(); return desc; } } } override 키워드는 EquilateralTriangle.getArea() 메서드로 RegularPolygon 수퍼 클래스의 getArea() 메서드를 재정의하려는 의도를 나타냅니다. EquilateralTriangle.getArea() 메서드가 호출되면 위 코드의 공식을 사용하여 면적을 계산하며 RegularPolygon.getArea() 메서드의 코드는 결코 실행되지 않습니다. 한편 EquilateralTriangle 클래스에 getPerimeter() 메서드의 자체 버전은 정의되어 있지 않습니다. EquilateralTriangle.getPerimeter() 메서드가 호출되면 이 호출은 상속 체인을 따라 위로 전달되어 RegularPolygon 클래스의 getPerimeter() 메서드의 코드가 실행됩니다. EquilateralTriangle() 생성자에서는 super() 문을 사용하여 수퍼 클래스의 RegularPolygon() 생성자를 명시적으로 호출합니다. 두 생성자의 매개 변수 집합이 동일한 경우에는 EquilateralTriangle() 생성자를 완전히 생략할 수 있으며 이렇게 하면 RegularPolygon() 생성자가 대신 실행됩니다. 그러나 RegularPolygon() 생성자에서는 numSides라는 추가적인 매개 변수가 필요합니다. 따라서 EquilateralTriangle() 생성자에서는 super(len, 3)을 호출하여 len 입력 매개 변수와 값 3을 전달함으로써 삼각형의 변이 3개임을 알립니다. describe() 메서드에서도 super() 문을 사용하지만 다른 방법이 적용됩니다. 이 방법에서는 super() 문을 사용하여 describe() 메서드의 RegularPolygon 수퍼 클래스 버전을 호출합니다. EquilateralTriangle.describe() 메서드에서는 먼저 desc 문자열 변수를 형태 유형에 대한 명령문으로 설정합니다. 그런 다음 super.describe()를 호출하여 RegularPolygon.describe() 메서드의 결과를 구하고 이 결과를 desc 문자열에 추가합니다. Square 클래스는 여기서 자세히 설명되지는 않지만 생성자와 getArea() 및 describe() 메서드의 자체 구현을 제공한다는 점에서 EquilateralTriangle 클래스와 유사합니다. 다형성 및 팩토리 메서드인터페이스 및 상속을 잘 활용하는 클래스의 집합은 여러 가지 흥미로운 방식으로 사용될 수 있습니다. 예를 들어 지금까지 설명한 모든 형태 클래스는 IGeometricShape 인터페이스를 직접 구현하거나 이 인터페이스를 구현한 수퍼 클래스를 확장합니다. 따라서 변수를 IGeometricShape의 인스턴스로 정의하면 describe() 메서드를 호출하기 위해 해당 인스턴스가 실제로 Circle 클래스의 인스턴스인지 아니면 Square 클래스의 인스턴스인지 알아야 할 필요가 없습니다. 다음 코드에서는 이에 대한 예제를 보여 줍니다. var myShape:IGeometricShape = new Circle(100); trace(myShape.describe()); 변수가 IGeometricShape 인터페이스의 인스턴스로 정의되어 있지만 기반 클래스는 Circle이므로 myShape.describe()를 호출하면 Circle.describe()가 실행됩니다. 이 예제에서는 다형성의 원리가 적용됨을 보여 줍니다. 정확히 동일한 메서드를 호출해도 메서드가 호출된 객체의 클래스에 따라 다른 코드가 실행되는 것을 확인할 수 있습니다. GeometricShapes 응용 프로그램에서는 팩토리 메서드로 알려진 디자인 패턴의 단순화된 버전을 사용하여 이러한 종류의 인터페이스 기반 다형성을 적용합니다. 팩토리 메서드라는 용어는 기본 데이터 유형이나 내용이 컨텍스트에 따라 달라질 수 있는 객체를 반환하는 함수를 의미합니다. 여기에 나와 있는 GeometricShapeFactory 클래스에는 createShape()라는 이름의 팩토리 메서드가 정의되어 있습니다. package com.example.programmingas3.geometricshapes { public class GeometricShapeFactory { public static var currentShape:IGeometricShape; public static function createShape(shapeName:String, len:Number):IGeometricShape { switch (shapeName) { case "Triangle": return new EquilateralTriangle(len); case "Square": return new Square(len); case "Circle": return new Circle(len); } return null; } public static function describeShape(shapeType:String, shapeSize:Number):String { GeometricShapeFactory.currentShape = GeometricShapeFactory.createShape(shapeType, shapeSize); return GeometricShapeFactory.currentShape.describe(); } } } createShape() 팩토리 메서드에서는 형태 하위 클래스의 생성자에서 생성되는 인스턴스의 세부 사항을 정의하도록 하는 한편 새 객체를 반환할 때는 응용 프로그램에서 더 일반적인 방식으로 이 객체를 처리할 수 있도록 IGeometricShape 인스턴스로 반환합니다. 이전 예제의 describeShape() 메서드는 응용 프로그램에서 팩토리 메서드를 사용하여 더 구체적인 객체에 대한 일반적인 참조를 얻을 수 있는 방법을 보여 줍니다. 응용 프로그램에서는 새로 생성된 Circle 객체에 대한 설명을 다음과 같이 확인할 수 있습니다. GeometricShapeFactory.describeShape("Circle", 100); 그런 다음 describeShape() 메서드에서는 동일한 매개 변수를 사용하여 createShape() 팩토리 메서드를 호출하고 반환된 새 Circle 객체를 currentShape라는 정적 변수에 저장합니다. 이 변수의 유형은 IGeometricShape 객체입니다. 이어서 currentShape 객체에 대해 describe() 메서드를 호출하면 자동으로 Circle.describe() 메서드가 실행되고 이 메서드에서는 원에 대한 상세한 설명을 반환합니다. 샘플 응용 프로그램 개선응용 프로그램을 개선하거나 변경하면 인터페이스 및 상속의 진정한 효과가 분명하게 드러납니다. 이 샘플 응용 프로그램에 새 형태로 5각형을 추가하려는 경우를 가정해 봅니다. RegularPolygon 클래스를 확장하고 getArea() 및 describe() 메서드의 자체 버전을 정의하는 Pentagon 클래스를 만듭니다. 그런 다음 응용 프로그램의 사용자 인터페이스에 있는 콤보 상자에 Pentagon 옵션을 새로 추가합니다. 더 이상의 작업은 필요하지 않습니다. Pentagon 클래스에서는 상속을 통해 RegularPolygon 클래스의 getPerimeter() 메서드 및 getSumOfAngles() 메서드의 기능을 자동적으로 사용합니다. Pentagon 클래스는 IGeometricShape 인터페이스를 구현한 클래스에서 상속되었으므로 Pentagon 인스턴스를 IGeometricShape 인터페이스로도 취급할 수 있습니다. 따라서 새 유형의 모양을 추가하기 위해 GeometricShapeFactory 클래스에 있는 메서드의 메서드 서명을 변경할 필요는 없으며 GeometricShapeFactory 클래스를 사용하는 코드를 변경할 필요도 없습니다. Pentagon 클래스를 Geometric Shapes 예제에 추가하는 연습을 해 보십시오. 이 연습을 통해 응용 프로그램에 새 기능을 추가할 때의 작업 로드가 인터페이스와 상속으로 인해 얼마나 줄어들 수 있는지 확인하십시오. |
|