구성과 상속이 동일합니까? 컴포지션 패턴을 구현하려면 Java로 어떻게 할 수 있습니까?
구성과 상속이 동일합니까? 컴포지션 패턴을 구현하려면 Java로 어떻게 할 수 있습니까?
답변:
그들은 절대적으로 다릅니다. 상속은 "is-a" 관계입니다. 작곡은 "has-a" 입니다.
C
확장하는 대신 다른 클래스의 인스턴스를 클래스 의 필드로 사용하여 작성합니다 C
. 컴포지션이 상속보다 훨씬 더 나은 좋은 예는 java.util.Stack
현재 확장됩니다 java.util.Vector
. 이것은 이제 실수로 간주됩니다. 스택 "is-not-a" 벡터; 요소를 임의로 삽입하거나 제거해서는 안됩니다. 대신 구성이되어야합니다.
불행히도 상속 계층 구조를 변경하면 기존 코드와의 호환성이 손상되므로이 디자인 실수를 수정하기에는 너무 늦습니다. 있었다 Stack
상속 대신 사용 조성, 항상 API를 위반하지 않고 다른 데이터 구조를 사용하도록 수정 될 수있다 .
Josh Bloch의 책 Effective Java 2nd Edition을 강력히 추천합니다.
좋은 객체 지향 디자인은 기존 클래스를 자유롭게 확장하는 것이 아닙니다. 첫 번째 본능은 대신 작성하는 것입니다.
또한보십시오:
구성 수단 HAS A
상속 수단IS A
Example
: 자동차 에는 엔진이 있고 자동차 는 자동차입니다
프로그래밍에서 이것은 다음과 같이 표현됩니다.
class Engine {} // The Engine class.
class Automobile {} // Automobile class which is parent to Car class.
class Car extends Automobile { // Car is an Automobile, so Car class extends Automobile class.
private Engine engine; // Car has an Engine so, Car class has an instance of Engine class as its member.
}
:-/
type
유형의 분야를 가질 수 있습니다Enum
상속은 어떻게 위험 할 수 있습니까?
예를 들어 보자
public class X{
public void do(){
}
}
Public Class Y extends X{
public void work(){
do();
}
}
1) 위 코드에서 알 수 있듯이 클래스 Y는 클래스 X와 매우 강한 결합을 갖습니다. 수퍼 클래스 X에서 변경된 사항이 있으면 Y가 급격히 중단 될 수 있습니다. 미래에 클래스 X가 아래 서명으로 메소드 작업을 구현한다고 가정하십시오.
public int work(){
}
클래스 X에서 변경이 수행되지만 클래스 Y를 컴파일 할 수 없게됩니다. 따라서 이러한 종류의 종속성은 모든 수준으로 올라갈 수 있으며 매우 위험 할 수 있습니다. 수퍼 클래스가 모든 서브 클래스 내부의 코드에 대한 완전한 가시성을 갖지 못할 때마다 서브 클래스는 수퍼 클래스에서 일어나는 일을 항상 인식 할 수 있습니다. 따라서이 강력하고 불필요한 커플 링을 피해야합니다.
구성이이 문제를 어떻게 해결합니까?
같은 예제를 수정하여 볼 수 있습니다
public class X{
public void do(){
}
}
Public Class Y{
X x = new X();
public void work(){
x.do();
}
}
여기서는 Y 클래스에서 X 클래스의 참조를 작성하고 X 클래스의 인스턴스를 작성하여 X 클래스의 메소드를 호출합니다. 이제 강력한 결합이 사라졌습니다. 슈퍼 클래스와 서브 클래스는 서로 매우 독립적입니다. 클래스는 상속 상황에서 위험한 변경을 자유롭게 할 수 있습니다.
2) 컴포지션 호출 유연성을 제공한다는 점에서 컴포지션의 두 번째 장점은 다음과 같습니다.
class X implements R
{}
class Y implements R
{}
public class Test{
R r;
}
r 참조를 사용하는 테스트 클래스에서 Y 클래스뿐만 아니라 X 클래스의 메소드를 호출 할 수 있습니다. 이 유연성은 상속에 없었습니다
3) 또 다른 큰 장점 : 단위 테스트
public class X {
public void do(){
}
}
Public Class Y {
X x = new X();
public void work(){
x.do();
}
}
위의 예에서 x 인스턴스의 상태를 알 수없는 경우 일부 테스트 데이터를 사용하여 쉽게 조롱 할 수 있으며 모든 방법을 쉽게 테스트 할 수 있습니다. 인스턴스의 상태를 얻고 메소드를 실행하기 위해 수퍼 클래스에 크게 의존했기 때문에 이것은 전혀 상속이 불가능했습니다.
4) 상속을 피해야하는 또 다른 이유는 Java가 다중 상속을 지원하지 않기 때문입니다.
이것을 이해하기 위해 예제를 보자.
Public class Transaction {
Banking b;
public static void main(String a[])
{
b = new Deposit();
if(b.deposit()){
b = new Credit();
c.credit();
}
}
}
알아 둘만 한 :
상속은 컴파일 타임에 기능을 제공하는 동안 런타임에 쉽게 달성됩니다.
구성은 HAS-A 관계라고도하며 상속은 IS-A 관계라고도합니다.
따라서 위의 여러 가지 이유로 항상 상속보다 구성을 선호하는 습관을들이십시오.
@Michael Rodrigues의 답변이 정확하지 않으며 (죄송합니다. 직접 의견을 말할 수 없습니다) 약간의 혼란을 초래할 수 있습니다.
인터페이스 구현 은 상속의 한 형태입니다. 인터페이스를 구현할 때 모든 상수를 상속 할뿐만 아니라 객체가 인터페이스에 의해 지정된 유형이되도록 커밋하고 있습니다. 여전히 " is-a "관계입니다. 자동차가 Fillable을 구현 하면 자동차 는 " Fillable " 이며 , Fillable을 사용하는 곳이라면 어디에서나 코드에 사용할 수 있습니다 .
구성은 상속과 근본적으로 다릅니다. 컴포지션을 사용할 때 상속을 사용할 때 만드는 " is-a "관계 와 반대로 (다른 답변 에서 볼 수 있듯이) 두 객체간에 " has-a "관계 를 만드는 것 입니다.
따라서 다른 질문의 자동차 예에서 자동차 에 "가스 탱크 가 있습니다 "라고 말하고 싶다면 다음과 같이 구성을 사용합니다.
public class Car {
private GasTank myCarsGasTank;
}
잘하면 그것은 오해를 해결합니다.
상속 은 IS-A 관계를 이끌어냅니다 . 구성 은 HAS-A 관계를 가져옵니다 . 전략 패턴은 컴포지션이 특정 동작을 정의하는 알고리즘 계열이있는 경우에 사용해야한다고 설명합니다.
비행 행동을 구현하는 오리 클래스의 전형적인 예.
public interface Flyable{
public void fly();
}
public class Duck {
Flyable fly;
public Duck(){
fly = new BackwardFlying();
}
}
따라서 비행을 구현하는 여러 클래스를 가질 수 있습니다.
public class BackwardFlying implements Flyable{
public void fly(){
Systemout.println("Flies backward ");
}
}
public class FastFlying implements Flyable{
public void fly(){
Systemout.println("Flies 100 miles/sec");
}
}
상속을 위해서라면, 파리 기능을 반복해서 구현하는 두 종류의 새가있을 것입니다. 따라서 상속과 구성은 완전히 다릅니다.
작곡은 소리 그대로입니다. 부품을 연결하여 객체를 만듭니다.
이 답변의 나머지 부분은 다음 전제에 따라 잘못 수정되었습니다 .
이것은 인터페이스로 수행됩니다.
예를 Car
들어 위 의 예를 사용하면
Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel
따라서 몇 가지 표준 이론적 구성 요소를 사용하면 객체를 만들 수 있습니다. 그런 다음 House
보호자가 탑승자를 보호 하는 방법과 보호자가 탑승자를 보호 하는 방법을 작성하는 것이 귀하의 임무 Car
입니다.
상속은 다른 방법과 같습니다. 완전한 (또는 반 완전한) 객체로 시작하여 변경하려는 다양한 비트를 바꾸거나 재정의합니다.
예를 들어, 방법 및 방법 MotorVehicle
이 제공 될 수 있습니다 . 모터 사이클과 자동차를 채우는 것과 동일하기 때문에 연료 방법을 그대로 둘 수 있지만 모터 사이클 이과 다르게 구동되므로이 방법을 무시할 수 있습니다 .Fuelable
Drive
Drive
Car
상속을 통해 일부 클래스는 이미 완전히 구현되었으며 다른 클래스에는 사용자가 재정의해야하는 메서드가 있습니다. 작곡으로 당신에게 아무것도주지 않습니다. (하지만 주위에 무언가가있는 경우 다른 클래스에서 메소드를 호출하여 인터페이스를 구현할 수 있습니다).
컴포지션은 iUsesFuel과 같은 방법을 사용하는 경우 자동차 여부에 관계없이 연료를 공급할 수있는 물체를 다루는 것에 대해 걱정하는 다른 방법 (다른 클래스, 다른 프로젝트)을 가질 수 있기 때문에 더 유연하게 보입니다. 보트, 스토브, 바베큐 등. 인터페이스는 인터페이스를 구현한다고 말하는 클래스가 실제로 해당 인터페이스에 관한 모든 메소드를 갖도록 요구합니다. 예를 들어
iFuelable Interface:
void AddSomeFuel()
void UseSomeFuel()
int percentageFull()
다른 방법을 사용할 수 있습니다
private void FillHerUp(iFuelable : objectToFill) {
Do while (objectToFill.percentageFull() <= 100) {
objectToFill.AddSomeFuel();
}
이상한 예제이지만 객체가 구현하기 때문에이 메소드가 채우는 것을 신경 쓰지 않는다는 것을 보여줍니다 iUsesFuel
. 이야기의 끝.
대신 상속을 사용하는 경우, 당신은 다른 필요 FillHerUp
를 다루는 방법을 MotorVehicles
하고 Barbecues
당신이 어떤에서 상속에 다소 이상한 "ObjectThatUsesFuel"기본 객체를 가지고하지 않는 한,.
ThisCase
아닌로 작성된다고 명시하고 있습니다 camelCase
. 따라서 인터페이스 IDrivable
등의 이름을 지정하는 것이 가장 좋습니다 . 모든 인터페이스를 패키지로 올바르게 다시 그룹화하면 "I"가 필요하지 않을 수 있습니다.
구성과 상속이 동일합니까?
그들은 동일하지 않습니다.
구성 : 개체 그룹을 개체의 단일 인스턴스와 동일한 방식으로 처리해야합니다. 컴포지트의 목적은 개체를 트리 구조로 "구성하여" 전체 계층 구조를 나타냅니다.
상속 : 클래스는 직접적이든 간접적이든 상관없이 모든 수퍼 클래스에서 필드와 메소드를 상속합니다. 서브 클래스는 상속하는 메소드를 대체하거나 상속 된 필드 나 메소드를 숨길 수 있습니다.
컴포지션 패턴을 구현하려면 Java로 어떻게 할 수 있습니까?
Wikipedia 기사는 Java에서 복합 패턴을 구현하기에 충분합니다.
주요 참가자 :
구성 요소 :
잎 :
복합 :
복합 패턴 을 이해하기위한 코드 예제 :
import java.util.List;
import java.util.ArrayList;
interface Part{
public double getPrice();
public String getName();
}
class Engine implements Part{
String name;
double price;
public Engine(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Trunk implements Part{
String name;
double price;
public Trunk(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Body implements Part{
String name;
double price;
public Body(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Car implements Part{
List<Part> parts;
String name;
public Car(String name){
this.name = name;
parts = new ArrayList<Part>();
}
public void addPart(Part part){
parts.add(part);
}
public String getName(){
return name;
}
public String getPartNames(){
StringBuilder sb = new StringBuilder();
for ( Part part: parts){
sb.append(part.getName()).append(" ");
}
return sb.toString();
}
public double getPrice(){
double price = 0;
for ( Part part: parts){
price += part.getPrice();
}
return price;
}
}
public class CompositeDemo{
public static void main(String args[]){
Part engine = new Engine("DiselEngine",15000);
Part trunk = new Trunk("Trunk",10000);
Part body = new Body("Body",12000);
Car car = new Car("Innova");
car.addPart(engine);
car.addPart(trunk);
car.addPart(body);
double price = car.getPrice();
System.out.println("Car name:"+car.getName());
System.out.println("Car parts:"+car.getPartNames());
System.out.println("Car price:"+car.getPrice());
}
}
산출:
Car name:Innova
Car parts:DiselEngine Trunk Body
Car price:37000.0
설명:
구성 및 상속의 장단점에 대해서는 아래 질문을 참조하십시오.
단순 단어 집합에서 관계가 있음을 의미합니다.
구성은 특별한 집계의 경우입니다 . 보다 구체적인 방식으로 제한된 집계를 구성이라고합니다. 객체에 다른 객체가 포함 된 경우 컨테이너 객체가 없으면 포함 된 객체가 존재할 수없는 경우 컴포지션이라고합니다. 예 : 수업에는 학생이 포함됩니다. 수업이없는 학생은 존재할 수 없습니다. 수업과 학생들 사이에는 작문이 있습니다.
집계를 사용하는 이유
코드 재사용 성
집계를 사용하는 경우
관계 선이없는 경우 집계로 코드 재사용이 가장 잘 이루어집니다
계승
상속은 부모 자녀 관계입니다. 상속 수단은 RelationShip입니다.
Java의 상속은 한 객체가 부모 객체의 모든 속성과 동작을 얻는 메커니즘입니다.
Java 1 코드 재사용 성에서 상속 사용. 2 하위 클래스 및 메소드 재정의에 추가 기능을 추가하여 런타임 다형성을 달성 할 수 있습니다.
Inheritance와 Composition이 코드 재사용 성을 제공하지만 Java에서 Composition과 Inheritance의 주요 차이점은 Composition은 코드를 확장하지 않고도 코드를 재사용 할 수 있지만 상속을 위해서는 코드 나 기능을 재사용하기 위해 클래스를 확장해야한다는 것입니다. 이 사실과 다른 점은 Composition을 사용하면 확장 할 수 없지만 최종 클래스의 코드를 재사용 할 수 있지만 이러한 경우 상속은 코드를 재사용 할 수 없다는 것입니다. 또한 Composition을 사용하면 멤버 변수로 선언 된 많은 클래스의 코드를 재사용 할 수 있지만 상속을 사용하면 Java에서 다중 상속이 지원되지 않기 때문에 하나의 클래스 만 확장 할 수 있기 때문에 하나의 클래스로만 코드를 재사용 할 수 있습니다 . 하나의 클래스가 두 개 이상의 클래스를 확장 할 수 있기 때문에 C ++에서이를 수행 할 수 있습니다. BTW, 당신은 항상해야합니다Java의 상속보다 작문을 선호합니다. 저뿐만 아니라 Joshua Bloch 도 그의 책에서 제안했습니다.
이 예제는 상속 과 구성 의 차이점을 명확하게 설명한다고 생각합니다 .
이 예제에서는 상속과 구성을 사용하여 문제를 해결합니다. 저자는 다음과 같은 사실에주의를 기울입니다. 상속 에서 수퍼 클래스의 변경 은 상속 된 클래스에서 상속을받는 문제를 일으킬 수 있습니다.
상속 또는 컴포지션에 UML을 사용할 때 표현의 차이를 볼 수도 있습니다.
상속 대 구성.
상속과 구성은 모두 재사용과 수업 행동의 확장에 사용됩니다.
IS-A 관계 유형과 같은 패밀리 알고리즘 프로그래밍 모델에서 주로 사용되는 상속은 유사한 종류의 객체를 의미합니다. 예.
이들은 자동차 가족에 속합니다.
컴포지션은 HAS-A 관계 유형을 나타냅니다. Duster has Five Gears, Safari에는 4 개의 Gears 등이 있습니다. 기존 클래스의 기능을 확장해야 할 때 컴포지션을 사용합니다. 예 를 들어 Duster 오브젝트에 기어를 하나 더 추가 한 다음 기어 오브젝트를 하나 더 작성하여 Duster 오브젝트에 작성해야합니다.
파생 클래스가 모두 해당 기능을 필요로하지 않는 한 기본 클래스를 변경해서는 안됩니다.이 시나리오에서는 Composition.Such를 사용해야합니다.
클래스 A에 의해 파생 된 클래스 A
클래스 C에 의해 파생 된 클래스 A
클래스 D에 의해 파생 된 클래스 A
클래스 A에 기능을 추가하면 클래스 C와 D에 해당 기능이 필요하지 않은 경우에도 모든 하위 클래스에서 사용할 수 있습니다.이 시나리오에서는 해당 기능에 대해 별도의 클래스를 작성하여 필요한 클래스에 작성해야합니다 ( 여기에 클래스 B가 있습니다).
아래는 예입니다.
// This is a base class
public abstract class Car
{
//Define prototype
public abstract void color();
public void Gear() {
Console.WriteLine("Car has a four Gear");
}
}
// Here is the use of inheritence
// This Desire class have four gears.
// But we need to add one more gear that is Neutral gear.
public class Desire : Car
{
Neutral obj = null;
public Desire()
{
// Here we are incorporating neutral gear(It is the use of composition).
// Now this class would have five gear.
obj = new Neutral();
obj.NeutralGear();
}
public override void color()
{
Console.WriteLine("This is a white color car");
}
}
// This Safari class have four gears and it is not required the neutral
// gear and hence we don't need to compose here.
public class Safari :Car{
public Safari()
{ }
public override void color()
{
Console.WriteLine("This is a red color car");
}
}
// This class represents the neutral gear and it would be used as a composition.
public class Neutral {
public void NeutralGear() {
Console.WriteLine("This is a Neutral Gear");
}
}
아니오, 둘 다 다릅니다. 구성은 "HAS-A"관계를 따르고 상속은 "IS-A"관계를 따릅니다. 구성의 가장 좋은 예는 전략적 패턴이었습니다.
상속은 클래스의 완전한 기능을 재사용하는 것을 의미합니다. 여기 내 클래스는 수퍼 클래스의 모든 메소드를 사용해야하며 클래스는 수퍼 클래스와 밀접하게 결합되며 상속의 경우 코드가 두 클래스에 복제됩니다.
그러나 구성을 사용하여 다른 클래스와 대화 할 때 이러한 모든 문제를 극복 할 수 있습니다. 작문은 다른 클래스의 속성을 우리가 이야기하고 싶은 클래스로 선언합니다. 해당 속성을 사용하여 해당 클래스에서 원하는 기능을 얻을 수 있습니다.