본문 바로가기
우아한테크코스/2주차 프리코스

[Java] 2주차 스터디 - Getter와 Setter

by 행복한라이언 2023. 10. 31.
728x90
반응형


1. 접근 제어자

  해당 클래스 안에서 동일 패키지 안에서 동일 패키지 또는 자손 클래스 안에서 다른 패키지 포함 어느 곳에서든
public
protected  
default    
private      

 

1) 접근제어자를 왜 사용하는가? - GPT의 답변!

  1. 캡슐화: 클래스의 내부 데이터를 숨기고, 외부에서 직접 접근하지 못하게 함으로써 클래스의 안정성을 높입니다. 이를 통해 응집도를 높이고 결합도를 낮추어 코드의 유지 보수성을 향상시킵니다.
  2. 보안: 중요한 정보나 기능을 외부에서 접근할 수 없도록 보호합니다. 이를 통해 악의적인 사용자로부터 데이터를 보호할 수 있습니다.
  3. 모듈화: 코드를 모듈화하여 개발자가 더 작은 부분으로 작업할 수 있도록 합니다. 이는 대규모 프로젝트에서 여러 사람이 동시에 작업하는 경우 특히 중요합니다.
  4. 재사용성: 외부에서 접근할 수 있는 인터페이스를 제공하여 코드의 재사용성을 높입니다. 이는 객체 지향 프로그래밍의 핵심 개념 중 하나입니다.
  5. 유지 보수성: 코드를 관리하기 쉽고 유지 보수하기 쉽도록 만듭니다. 접근 제어자를 사용하여 클래스의 사용 방법을 명확히 정의함으로써 다른 개발자가 클래스를 이해하고 수정하는 데 도움을 줍니다.

2) private 접근 제어자

 

public class Player {
    private final String name;

    public Player(String playerName) {
        this.name = playerName;
    }
    //...
}

▷ private로 선언되어 "인스턴스.필드" 방식으로는 접근 불가 || static으로 선언되어도 "클래스명.필드" 방식으로 접근 불가

// Main.java

Player player = new Player("pobi");

player.name = "woni" // ⚠️ 불가

▷ private 접근 제어자는 캡슐화와 보안과 관련이 깊다.

  • 진짜로 `감추는’ 것이 아니다. - 코드로 확인 가능
  • 중요한 데이터에 대한 접근을 제어하여 의도치 않은 데이터 변경 방지 - 작성자의 의도대로 사용하도록 하기 위함

2. Getter와 Setter

1) 왜 사용해?

- private로 선언하면 외부에서 접근 및 수정이 불가능 

▷ getter와 setter를 정의하고 사용해 private로 선언된 필드에 접근 및 수정하기 위해서 사용됨.

 

2) 예시

public class Player {

    private String name;

    public Player(String playerName) {
        this.name = playerName;
    }
    
    public String getPlayerName() {
        return name;
    }
    public void setPlayerName(String name) {
        this.name = name;
    }
}

getter: private로 선언된 필드에 접근하기 위해서 사용

setter: private로 선언된 필드를 수정하기 위해서 사용

//getter, setter 기본모양

public <멤버 변수 자료형> get[필드명](){
	return <멤버 변수명>;
}

public void set[필드명]( <멤버변수 자료형> <변수명> ){
	this.멤버변수 = 매개변수;
}

직접 사용해보자.

public class Main {
    public static void main(String[] args) {
        Player player = new Player("pobi");
        System.out.println(player.getPlayerName()); // pobi

        player.setPlayerName("woni");
        System.out.println(player.getPlayerName()); // woni
    }
}

→ playerName에 대해 getPlayerName로 접근해 pobi를 출력했다.

→ setPlayerName을 통해 pobi 에서 woni로 이름을 변경했다.


3. 왜 Getter와 Setter의 사용을 지양하는가?

[출처 및 참고]

 

[OOP] Getter와 Setter는 지양하는게 좋다

목차 들어가기 전에 얼마 전 사내에서 Getter와 Setter를 함부로 사용하면 안되는 이유에 대한 세미나가 있었다. Setter에 대한 이야기는 워낙 많이 알려져있었지만 Getter에 대한 이야기는 잘 하지 않

colabear754.tistory.com

 

이유와 솔루션으로 정리하는 객체지향 생활체조 원칙

객체지향 생활체조 원칙 소트웍스 앤솔러지 표지 "어떤 멍청이라도 컴퓨터가 이해할 수 있는 코드는 작성할 수 있다. 좋은 프로그래머는 사람이 이해할 수 있는 코드를 작성한다. (Any fool can write

hudi.blog

 

1) Getter와 Setter를 지양하는 이유

 간단하게 생각해보면 특정 필드를 private으로 선언한 이유가 있을 것이다. 그런데 public으로 선언된 getter와 setter 덕분(?) 때문(?)에 접근 및 수정이 가능해지면 우리가 private로 선언한 목적 - 필드를 감추고 클래스의 캡슐화 및 보안 유지 - 에 부합하는지 생각해볼 필요가 있다.

 

2) Getter와 Setter를 지양하는 이유 - Setter

 특히 Setter는 왜 사용을 지양해야하는가에 대해서 쉽게 와닿는다. 위의 예시를 보면 pobi에서 woni로 setPlayerName을 통해서 name필드를 변경했다. 그런데 왜 변경했는지 의도가 쉽게 파악되지 않는다. 바꾼 나는 알겠지만 내 코드를 본 누군가는 왜 바꿨는가에 대해서 의문을 가질 가능성이 농후하다.

public class Main {
    public static void main(String[] args) {
        Player player = new Player("pobi");
        System.out.println(player.getPlayerName()); // pobi

        player.setPlayerName("woni"); // 왜 바꾸는데??
        System.out.println(player.getPlayerName()); // woni
    }
}

 

3) Getter와 Setter를 지양하는 이유 - Getter 

 Setter에 비해서 Getter를 지양하는 이유는 쉽게 와닿지 않는다. Getter는 Setter에 비해서 자유롭게 사용할 수 있다고 생각한다. 다만, 메서드의 인자로 넘길 값이 필요해서 사용하는 경우 외에 Getter로 조회한 값을 조작하는 경우가 적절하지 않다고 볼 수 있다.

class Player {

    private String name;
    private int strength;

    public Player(String playerName, int strength) {
        this.name = playerName;
        this.strength = strength;
    }

    public String getPlayerName() {
        return name;
    }

    public int getStrength() {
        return strength;
    }

}

class Main {
    public static void main(String[] args) {
        boolean drinkLuckyPotion = true;
        Player player = new Player("pobi", 10);
        System.out.println(player.getPlayerName()); // pobi
        System.out.println(player.getStrength()); // 10

        if (drinkLuckyPotion) {
            int playerStrength = player.getStrength() * 2;
        }
        else{
            int playerStrength = player.getStrength() / 2;
        }

    }
}

1) Getter메서드를 사용함으로써 Player의 내부 상태, 구현을 외부로 노출시키는 문제 발생!

▷  Main 클래스의 if-else 문에서 player의 strength를 가져와서 증가 또는 감소, 내부에 직접 접근하는 것과 동일

 

2) 럭키포션을 마셔서 힘을 조절하는 하는 행위는 Player 객체의 책임


4. Getter와 Setter 

 객체 내부에서 최대한 담당할 수 있도록 내부 메서드로 구현하자!

1) Setter

▷  의도를 반영한 메서드를 구현! '우테코 공통 피드백 - 이름을 통해 의도를 드러낸다 / 축약하지 않는다.'을 통해서도 알 수 있듯이 나의 의도, 목적을 잘 드러내는 것이 매우 중요하기 때문에 Setter 대신 명확한 의도가 보이는 메서드로 구현하도함.

class Player {

    private String name;
    private int strength;

    public Player(String playerName, int strength) {
        this.name = playerName;
        this.strength = strength;
    }

    public String getPlayerName() {
        return name;
    }
    //...
    public void changeName(String newName) {
        this.name = newName;
    }

}

class Main {
    public static void main(String[] args) {
        boolean drinkLuckyPotion = true;
        Player player = new Player("pobi", 10);
        System.out.println(player.getPlayerName()); // pobi
  		//...
        if (isNameChangeEvent){
            player.changeName("woni");
        }
    }
}

2) Getter

▷  Player 클래스 내부에 구현!. 이를 통해서 Player 내부에 직접 접근하지 않고 간접적으로 값을 변경할 수 있도록 함.

package boj;

class Player {

    private String name;
    private int strength;

    public Player(String playerName, int strength) {
        this.name = playerName;
        this.strength = strength;
    }

    public String getPlayerName() {
        return name;
    }

    public int getStrength() {
        return strength;
    }

    public void adjustStrength(boolean drinkLuckyPotion) {
        if (drinkLuckyPotion) {
            this.strength *= 2; // strength를 2배로 증가
        } else {
            this.strength /= 2; // strength를 2로 나누어 감소
        }
    }

}

class Main {
    public static void main(String[] args) {
        boolean drinkLuckyPotion = true;
        Player player = new Player("pobi", 10);
        System.out.println(player.getPlayerName()); // pobi
        System.out.println(player.getStrength()); // 10

        player.adjustStrength(drinkLuckyPotion); // strength 조정 메서드 호출

        System.out.println(player.getStrength()); // 변경된 strength 값 출력

    }
}
728x90
반응형