자신의 데이터를 사용해 하나의 역할(기능)을 수행하는 독립된 단위

public interface Vehicle {
public abstract void start(); // public abstract 키워드 생략 가능
void moveForward();
void moveBackward();
}
public class Car implements Vehicle {
@Override
void moveForward(){
System.out.println("자동차가 앞으로 갑니다.");
}
@Override
void moveBackward(){
System.out.println("자동차가 뒤로 갑니다.");
}
}
인터페이스(interface)에는 추상적인 개념만을 규정하고, 실제적인 구현은 해당 인터페이스를 구현하는 클래스가 한다.
추상화로 인해 보다 유연하고 변경에 열려있는 프로그램을 만들 수 있다.
부모 클래스에서는 선언만 하며 자식 클래스에서 부모 객체의 구체적인 내용을 오버라이딩(재정의)해서 사용한다.
부모의 메서드를 반드시 오버라이딩(재정의)해야 한다.
하위 클래스에게 구체적인 구현을 하도록 강제한다.
다중 상속을 지원한다.
인터페이스(interface) 상속에 사용된다.
public class Son implements Father, Mother{...}
부모 클래스에서 선언 / 정의(내용)을 모두 할 수 있고, 자식은 부모의 속성과 메서드를 그대로 사용 가능
오버라이딩(재정의)은 선택적이다.
다중 상속을 지원하지 않는다.
일반 클래스와 abstract 클래스 상속에 사용된다.
public class Car extends Vehicle {
boolean isConvertible;
void openWindow(){
System.out.println("창문을 엽니다.");
}
@Override
void moveForward(){
System.out.println("자동차가 앞으로 갑니다.");
}
@Override
void moveBackward(){
System.out.println("자동차가 뒤로 갑니다.");
}
}
@Override
void moveForward(){
System.out.println("자동차가 앞으로 갑니다.");
}
public class Main{
public static void main(String[] args){
// 원래 사용했던 객체 생성 방식
Car car = new Car();
MotorBike motorBike = new MotorBike();
// 다형성을 활용한 객체 생성 방식
Vehicle car2 = new Car();
Vehicle motorBike2 = new MotorBike();
}
}
public class Main{
public static void main(String[] args){
Vehicle[] vehicle = new Vehicle[2];
vehicle[0] = new Car();
vehicle[1] = new MotorBike();
for(Vehicle vehicle : vehicles) {
System.out.println(vehicle.getClass()); // 각각의 클래스 호출
// 호출값
// class Car
// class MotorBike
}
}
}
// 다형성을 적용하지 않았을 때
public class Driver {
void drive(Car car){
car.moveForward();
car.moveBackward();
}
void drive(MotorBike motorBike){
motorBike.moveForward();
motorBike.moveBackward();
}
}
// 다형성을 적용했을 때
public class Driver{
void drive(Vehicle vehicle){ // 매개 변수로 인터페이스 타입의 참조변수를 전달
vehicle.moveForward();
vehicle.moveBackward();
}
}
// DI를 적용하지 않았을 때
public static void main(String[] args){
// new Car와 new MotorBike처럼 객체에 직접적으로 의존하고 있어서,
// 해당 객체를 다른 객체로 변경할 시 코드의 변경이 불가피하다.
Vehicle car = new Car(); // 높은 결합도
Vehicle motorBike = new MotorBike(); // 높은 결합도
Driver driver = new Driver();
driver.drive(car);
driver.drive(motorBike);
}
// DI를 적용했을 때
public class Driver {
private final Vehicle vehicle;
public Driver(Vehicle vehicle) {
this.vehicle = vehicle;
}
void drive() {
vehicle.moveForward();
vehicle.moveBackward();
}
}
public static void main(String[] args){
Driver driver = new Driver(new Car());
driver.drive();
driver = new Driver(new MotorBike());
driver.drive();
}
이렇게 되면 Bus나 Boat 객체도 사용 가능해서 Driver 클래스의 재활용성이 높아진다.
테스트 할 때 FakeCar() 혹은 Food를 사용할 수도 있다.
서로 연관있는 속성과 기능들을 하나의 캡슐(capsule) 안에 담아서 데이터를 외부로부터 보호
- 객체 내부의 세부사항을 외부로부터 감추는 것(정보 은닉)
- 객체 간에는 데이터를 주고 받지 않는다.
- 목적 : 인터페이스만 공개해서 변경하기 쉬운 코드를 만드는 것
// getter / setter 적용 전
public class Car {
private String name;
priate String color;
public Car(String name, String color) {
this.model = model;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void moveForward() {
System.out.println("자동차가 앞으로 갑니다.");
}
public void moveBackward(){
System.out.println("자동차가 뒤로 갑니다.");
}
}
public class Driver {
private Car car;
public Driver(Car car) {
this.car = car;
}
// 만약 Car 클래스의 두 가지 메서드에 변경이 있으면,
// Driver 클래스의 drive 메서드도 수정해야 한다.
// 높은 결합도
void drive() {
car.moveForward();
car.moveBackward();
}
}
// getter / setter 적용 후
public class Car {
private String name;
priate String color;
public Car(String name, String color) {
this.model = model;
this.car = car;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
// public -> private
private void moveForward() {
System.out.println("자동차가 앞으로 갑니다.");
}
public void moveBackward(){
System.out.println("자동차가 뒤로 갑니다.");
}
public void operate() {
moveForward();
moveBackward();
}
}
public class Driver {
private Car car;
public Driver(Car car) {
this.car = car;
}
void drive() {
car.operate(); // Car 클래스에 있는 메서드를 간단하게 호출
}
}
Driver 클래스 입장에서는 Car 클래스의 내부 로직을 알 수도, 알 필요도 없졌다.
적절한 객체에게 적절한 책임을 할당하여 서로 메세지를 주고 받으며 협력하도록 하는것
점점 증가하는 소프트웨어의 복잡성을 낮추기 위해
유익한 자료 감사합니다.