#객체지향(OOP)의 특징
##추상화
- 공통의 속성이나 기능을 묶어 이름을 붙이는 것
- 추상화가 가능한 개채들은 개체가 소유한 특성의 이름으로 하나의 집합(class)를 이룬다.
- 객체지향적 관점에서 클래스를 정의하는 것도 추상화라고 정의가 가능하다.
- 예를들어 사과, 배, 포도라는 객체가 있을 때 이것들을
과일
으로 묶으며 추상화가 가능하다.
#캡슐화
- 낮은 결합도를 유지할 수 있도록 해주는 객체지향 설계원리.
- 정보은닉을 이용하여 결합도를 유연하게 한다.
public class ArrayStack{
public int top;
public int[] itemArray;
public int stackSize;
public ArrayStack(int stackSize){
itemArray = new int[stackSize];
top = -1;
this.stackSize = stackSize;
}
public boolean isEmpty(){
return (top == -1);
}
public boolean isFull(){
return (top == this.stackSize -1);
}
public void push(int item){
if(isFull()){
System.out.println("Insert Failed. Becouse Stack is Full!");
} else{
itemArray[++top] = item;
System.out.println("Insert item : " + item);
}
}
public int pop(){
if(isEmpty){
System.out.println("Delete Failed. Becouse Stack is Empty!");
return -1;
} else{
return itemArray[top--];
}
}
public int peek(){
if(isEmpty()){
System.out.println("Peeking Failed. Becouse Stack is Emplty!");
return -1;
} else{
return itemArray[top];
}
}
}
public class StackClient{
public void static main(String[] args){
ArrayStack stack = new ArrayStack(10);
stack.itemArray[++stack.top] = 20;
System.out.println(stack.itemArray[stack.top]);
}
}
/*
만약 캡슐화를 하지않는다면(모두 public으로 구현한다면)
ArrayStack에 구현한 push나 pop등을 쓰지 않고 StackClient에서 바로 배열에 접근이 가능하다.
이런 경우 두 클래스는 강한 결합이 발생한다.
예를들어 ArrayStack을 ArrayList로 변경하여 구현하면 StackClient 또한 변경해야한다.
*/
public class StackClient{ //ArrayStack클래스가 Array을 ArrayList로 변경되었을때
//StackClient또한 변경되어야한다.(만약 ArrayStack을 100군데에서 사용하면 100 다바꿔야함....)
public void static main(String[] args){
ArrayStack stack = new ArrayStack(10);
stack.itemArrayList.add(20);
System.out.println(stack.itemArrayList.size()-1);
}
}
만약 캡슐화를 하여 정보은닉 + 결합도를 낮춘다면 ArrayStack 내부가 변하던 말던 사용방법은 동일할 것이다. 따라서 소스를 변경하는 일은 발생하지 않음.
public class ArrayStack{
private int top; //정보은닉을 통해 외부에서 접근 불가능하도록 수정
private int[] itemArray; //정보은닉을 통해 외부에서 접근 불가능하도록 수정
private int stackSize; //정보은닉을 통해 외부에서 접근 불가능하도록 수정
public ArrayStack(int stackSize){
itemArray = new int[stackSize];
top = -1;
this.stackSize = stackSize;
}
public boolean isEmpty(){
return (top == -1);
}
public boolean isFull(){
return (top == this.stackSize -1);
}
public void push(int item){
if(isFull()){
System.out.println("Insert Failed. Becouse Stack is Full!");
} else{
itemArray[++top] = item;
System.out.println("Insert item : " + item);
}
}
public int pop(){
if(isEmpty){
System.out.println("Delete Failed. Becouse Stack is Empty!");
return -1;
} else{
return itemArray[top--];
}
}
public int peek(){
if(isEmpty()){
System.out.println("Peeking Failed. Becouse Stack is Emplty!");
return -1;
} else{
return itemArray[top];
}
}
}
public class StackClient{
public void static main(String[] args){
ArrayStack stack = new ArrayStack(10);
stack.push(20);
System.out.println(stack.peek());
}
}
/*
이로써 ArrayStack의 내부가 어떻게 변하던, 상관없이 동일한 메소드를 통해 사용이 가능해진다.
*/
#상속성(일반화 관계)
- 상속은 부모가 가지고 있는 재산(코드)를 자식에게 물려주는 것을 말한다.
- 상속을 받게 되면 하위 객체에서 상위 객체의 필드와 메소드를 사용 할 수 있다.
- 상속은 이미 잘 개발된 객체를 재사용해서 새로운 객체를 만들기 때문에 코드의 중복을 줄여준다.
상위객체 : value1필드와 getValue1()의 메소드로 구성
하위객체 : value2필드와 getValue2()의 메소드로 구성
만약 이와 같다면 하위객체에서 getValue1()이 호출이 가능하다.(부모의 메소드를 호출함)
- 따라서 동일한 기능을 또 구현할 필요 없이 코드를 재사용 할 수 있는 장점이 있음.
- 단 무분별한 상속은 빚(쓰지않는 코드)이 될 수 있다.
- 이를 방지하기위해
피터 코드의 상속 규칙
이 있다.
- 자식클래스와 부모클래스 사이는 '역할수행(is role played by'가 아니어야한다.
- 첫번째 규칙인 자식클래스가 부모 클래스의 역할 중 하나를 표현하는지 점검한다.
-
운전자
는 어떤 순간에사람
이 수행하는 역할의 하나다. - 마찬가지로회사원
또한 사람이 어떤 순간에 수행하는 역할의 하나다. - 따라서사람-운전자
또는사람-회사원
이 상속관계로 표현되어서는 안된다. - 한 클래스의 인스턴스는 다른 서브 클래스의 객체로 변환할 필요가 절대 없어야한다.
- 두번째 규칙인 자식 클래스의 인스턴스들 사이에 변환관계가 필요한지 점검한다.
-
운전자
는 어떤 시점에회사원
이 될 필요가 있으며회사원
또한운전자
가 될 필요가 있다. - 회사로 출퇴근하는 동안에는운전자
로서의 역할을 수행하며, 출근 후에는회사원
으로 역할을 수행한다. - 이런 경우 객체의 변환작업이 필요하므로 규칙에 위배된다. - 자식 클래스가 부모 클래스의 책임을 무시하거나 재정의하지 않고 확장만 수행해야한다.
- UML만 보아서는
사람
,운전자
,회사원
클래스에 어떤 속성과 연산의 정의되어있는지 알수가 없기 때문에 점검이 불가능하다. - 자식 클래스가 단지 일부 기능을 재사용할 목적으로 유틸리티 역할을 수행하는 클래스를 상속하지 않아야한다. - 기능만 재사용할 목적으로 상속 관계를 표현하지는 않았으므로 규칙을 준수한다.
- 자식클래스가 역할(role), 트랜잭션(transaction), 디바이스(device) 등을 특수화해야한다. - 슈퍼클래스가 역할, 트랜잭션, 디바이스를 표현하지않았으므로 규칙에 위배된다.
#다형성
- 서로 다른 클래스의 객체가 같은 메시지를 받았을 때 각자의 방식으로 동작하는 능력.
abstract class Pet {
public abstract void talk();
}
class Dog extends Pet {
public void talk(){...}
}
class Cat extends Pet {
public void talk(){...}
}
class Parrot extends Pet {
public void talk(){...}
}
class Main{
public static void gourpTalk(Pet[] p) {
int i;
for(i=0;i<p.size();i++){
p[i].talk();
}
}
public static void main(String[] args) {
Pet[] p = {new Dog(), new Cat(), new Parrot()};
groupTalk(p); //Dog, Cat, Parront에 정의된 talk가 각각 실행된다.
}
}
- 메소드 오버로딩
class Overloading {
public void print(String param) {
System.out.println(param + ": 문자열");
}
public void print(int param) {
System.out.println(param + ": 숫자");
}
}
class Main {
public static void main(String[] args) {
Overloading overLoading = new Overloading();
overLoading.print("123"); //"123: 문자열"
overLoading.print(123); //"123: 숫자"
}
}