우아한테크코스/2주차 프리코스

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

행복한라이언 2023. 10. 31. 14:04
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
반응형