접근제어란?
객체지향 프로그래밍 패러다임에서 캡슐화와 은닉화는 중요한 개념 중 하나이다. 은닉화를 구현하기 위한 핵심 기능이 바로 접근제어
이다. 접근제어는 코드끼리 상호작용을 할 때 파일 간 또는 모듈 간에 접근을 제한할 수 있는 기능이다.
가령 내가 정의한 코드를 다른 프로그래머가 접근하려할 때 접근제어를 통해 프로그래머에게 제공하고 싶은부분만 노출할 수 있으며 코드작성자의 의도대로 다른 프로그래머가 사용할 수 있도록 유도할 수있다.
Swift에서 명시할 수 있는 접근제어 키워드는 open, public, internal, fileprivate, private 이 있다. 각 타입(class, struct, enum), 타입 내부의 프로퍼티, 메서드, 이니셜라이저, 서브스크립트에 접근수준을 지정할 수 있다. enum에서 case의 접근수준은 enum의 접근수준을 따른다.
접근수준
접근수준 | 키워드 | 범위 | 비고 |
---|---|---|---|
개방 접근수준 | open | 모듈외부까지 | 클레스에서만 사용 |
공개 접근수준 | public | 모듈 외부까지 | |
내부 접근수준 | internal | 모듈 내부 | |
파일외부비공개 fileprivate | 테스트2 | 파일 내부 | |
비공개 접근수준 | private | 기능 정의 내부 |
public - 공개 접근수준
public 키워드는 모듈 내부, 모듈 내부, 파일, 기능단위 등 전 범위에서 사용이 가능하며 Swift의 기본 접근제어 수준은 public이다.
///공개수준으로 정의 되어있는 Fruit구조체 타입
public struct Fruit {
private var fruitName: String
private var fruitAmount: Int
init(name: String, amount: Int) {
self.fruitName = name
self.fruitAmount = amount
}
}
open - 개방 접근수준
open 키워드의 가장 큰 특징은 class와 그 멤버에게만 지정할 수 있다는 것이다. 공개 접근 수준과 차이점이 있다.
-
개방 접근수준의 클래스는 클래스가 정의된 모듈 밖에서도 상속을 받을 수 있지만 공개수준은 모듈 내부에서만 가능하다
-
개방 접근수준의 클래스는 클래스내의 멤버수정을 클래스가 정의된 보듈 밖에서도 할 수 있지만 공개수준은 모듈 내부에서만 가능하다.
개방 접근수준으로 클래스를 정의하는 것은 다른 모듈에서도 해당 클래스를 사용하겠다는 목적으로 작성했음을 의미한다.
///개방수준으로 정의 되어있는 Fruit구조체 타입
open class Fruit {
private var fruitName: String
private var fruitAmount: Int
init(name: String, amount: Int) {
self.fruitName = name
self.fruitAmount = amount
}
}
internal - 내부 접근수준
internal은 기본적으로 모든 요소에 암묵적으로 지정하는 기본 접근수준이다. 내부 접근수준으로 지정된 요소가 포함된 소스코드가 포함되는 모듈에서는 접근할 수 있다. 보통 외부 모듈에서 사용하지 않고 내부에서 광역적으로 사용할 때 지정한다.
fileprivate - 파일외부비공개 접근수준
fileprivate은 지정된 요소가 포함된 소스코드 파일 내에서만 접근할 수 잇다.
private - 비공개 접근수준
private으로 지정된 요소는 해당 요소가 포함된 범위에서만 사용가능하다. 같은 파일내부에서도 접근을 할 수 없다.
타입내의 프로퍼티는 항상 private으로 먼저 고려하는게 객체지향적으로 코드를 작성하는 방법이다.
읽기전용 구현
프로퍼티를 정의하면 저장 프로퍼티는 지정된 접근제한 범위 내에서 접근할 수 있다. 그렇다면 해당 프로퍼티에 대해서 접근은 가능하지만 해당 값을 변경할 수 없게 하려면 어떻게 해야할까?
설정자(setter)만 접근 수준을 갖도록 제한할 수 있다. 접근수준
(set)으로 표현한다면 해당 프로퍼티에 대하여 설정자의 접근 수준만 낮도록 지정할 수 잇다. 읽기전용(설정자 접근수준 제한)은 서브스크립트, 변수등에 적용될 수 있으며 해당 요소의 접근 수준보다 같거나 낮게 해야한다.
public struct Fruit {
private var fruitName: String
public var fruitAmount: Int
public private(set) var fruitColor: String
init(name: Stirng, amount: Int, color: String) {
self.fruitName = name
self.fruitAmount = amount
self.fruitColor = color
}
}
//생성자를 통한 접근은 접근자, 설정자 모두 가능하다
var apple = Fruit(name: "사과", amount:10, color: "빨강")
// 오류발생, private이므로 접근자에 접근이 불가능하므로 설정자또한 불가능
apple.fruitName
// 10 출력 성공
print(apple.fruitAmount)
//설정자에 접근하여 값 수정 성공
apple.fruitAmount = 20
// "red" 출력 성공
print(apple.fruitColor)
// 오류발생 설정자의 접근제한이 private이므로 수정 불가능
apple.fruitColor = "blue"
get, set을 이용한 읽기전용 구현
get,set을 사용할때는 저장 프로퍼티와 연산프로퍼티를 따로 정의해주어야한다. 이부분에 대해서는 나중에 자세히 다루겠다. 선언 방식만 보자면
private var count: Int =0
internal private(set) var calculatedcount: Int {
get {
return count
}
set {
count +=1
}
}
get은 접근자 set은 설정자이다. 위의 경우는 set은 private이므로 선언된 범위 외부에서 접근이 불가능하지만 접근자인 get은 internal이므로 해당 파일이 포함된 모듈에서 모두 접근가능하다.