- 프로그램의 여러 부분이 서로 의존하는 정도를 나타내는 커플링(Coupling)을 최소화하는 것이 중요 

       => 모듈화 스타일로 프로그램을 작성(프로그램을 더 작은 여러 모듈로 나눈다)

- 모듈 내부를 작업할 때는 같은 모듈을 가지고 작업하는 프로그래머와 협력

- 모듈 외부를 변경해야 하는 경우에만 다른 모듈을 가지고 작업하는 프로그래머와 협력

 

 

13.1 패키지 안에 코드 작성하기

1. package 절을 사용해 파일 전체를 패키지 안에 넣는다.

package bobsrockets.navigation

class Navigator

2. package 절 다음, 중괄호 안에 있는 정의를 모두 패키지에 넣는다.(패키징) => 한 파일 안에 여러 패키지

package bobsrockets {
   package navigation {    

     class Navigator
     package tests {
           class NavigatorSuite
      }
   }
}

 

 

13.2 관련 코드에 간결하게 접근하기

package bobsrockets {
   package navigation {
      class Navigator {
           val map = new StarMap     // 1 
       }
      class StarMap
   }
   class Ship {
          val nav = new navigation.Navigator       // 2
    }
    package fleets {
         class Fleet {    
           def addShip() = { new Ship }    // 3
         }
    }
}

1. 어떤 클래스가 속한 패키지 안에서는 접두사가 없어도 해당 클래스에 접근할 수 있다.

2. 어떤 패키지를 포함하는 패키지 안에서는 해당 패키지에 어떤 접두어도 붙이지 않고 접근할 수 있다.

3. 패키지 밖에서 접근 가능한 모든 이름을 그 패키지 안에서도 쓸 수 있다.

 

중괄호를 사용하기 싫다면 아래와 같이 중첩 패키지 구현 가능(연쇄 패키지 절)

package bobsrockets

package fleets

class Fleet{

     de addShip() = { new Ship }

}

 

Ex. 모든 최상위 패키지는 _root_ 패키지의 멤버로 취급

// In file lanch.scala
package launch {
class Booster3
}
// In file bobsrockets.scala
package bobsrockets {
  package navigation {
    package launch {
      class Booster1
    }
    class MissionControl {
      val booster1 = new launch.Booster1
      val booster2 = new bobsrockets.launch.Booster2
      val booster3 = new _root_.launch.Booster3
    }
  }
  package launch {
    class Booster2
  }
}

 

13.3 임포트

: 다른 패키지의 멤버에 접근할 때 전체 경로를 명시하지 않고 간단한 이름으로 접근하게 해준다.

 

Ex. 

package bobsdelights

abstract class Fruit(
  val name: String,
  val color: String
)

object Fruits {
  object Apple extends Fruit("apple", "red")
  object Orange extends Fruit("orange", "orange")
  object Pear extends Fruit("pear", "yellowish")
  val menu = List(Apple, Orange, Pear)
}

- import bobsdelights.Fruit  => 자바의 싱글 타입 임포트와 같다. Fruit에 간단하게 접근

- import bobsdelights._  => 자바의 import bobsdelights.* 와 같다. bobsdelights의 모든 멤버에 간단하게 접근

- import bobsdelights.Fruits._ => Fruits의 모든 멤버에 간단하게 접근

 

< 자바와 다른 스칼라의 유연한 import >

- 스칼라의 임포트는 코드의 어디에라도 들어갈 수 있다. 

- 임의의 값, 객체를 임포트할 수 있다.

def showFruit(fruit: Fruit) = {
   import fruit._
   println(name + "s are " + color)
}

 

- 패키지 자체도 임포트 가능

import java.util.regex      // 자바는 불가능하다. 자바는 java.util.regex.* 또는 java.util.regex.Pattern  
class AStarB {
   val pat = regex.Pattern.compile("a*b")
}

 

- 임포트 셀렉터를 통해 불러온 멤버 이름을 숨기거나 다른 이름을 지정할 수 있다.

import Fruits.{Apple, Orange}   // Fruits 객체에 있는 Apple과 Orange만 불러온다.

 

import Fruits.{Apple=>McIn, Orange}       // Apple 객체 이름을 McIn로 바꾼다. => Apple 객체는 McIn로 참조 

 

import Fruits.{Apple=>McIn, _}     // Fruits의 모든 멤버를 불러오나 Apple의 이름을 McIn으로 바꾼다.

 

import Fruits.{Pear => _ , _}     // Fruits에서 Pear를 제외한 모든 멤버를 불러온다.

 

13.4 암시적 임포트

스칼라는 모든 프로그램에 아래 임포트를 암묵적으로 추가한다.

import java.lang._     => java.lang.Thread 대신에 Thread 

import scala._          => 많이 사용하는 클래스 및 객체와 표준 스칼라 라이브러리, scala.List 대신에 List

import Predef._        => 암시적 변환 포함, Predef.assert 대신 assert

- 스칼라는 나중에 임포트한 패키지가 앞에서 임포트한 것을 가린다.

    Ex. StringBuilder 클래스는 scala 패키지와 java.lang 패키지에 있지만 scala.StringBuilder을 가리킨다.

 

 

13.5 접근 수식자

: 스칼라는 패키지, 클래스, 객체 멤버 앞에 private와 protected 접근 수식자를 두어 멤버에 대한 접근을 제한할 수 있다.

 

< 비공개 멤버 >

: 자바와 유사하지만 아래와 같은 예외 존재(자바는 외부 클래스가 자신의 내부 클래스에 있는 비공개 멤버에 접근 가능)

class Outer {
  class Inner {
      private def f() = { println("f") }
      class InnerMost {
          f() // OK
       }
   }
   (new Inner).f() // error: f is not accessible
}

 

< 보호 멤버 >

: 보호 멤버를 정의한 클래스의 서브 클래스에서만 멤버 접근 가능(자바보다 엄격)

package p {
   class Super {
      protected def f() = { println("f") }
   }
   class Sub extends Super {
      f()
   }
   class Other {
      (new Super).f() // error: f is not accessible
   }
}

 

< 공개 멤버 >

: private나 protected가 없는 멤버, public 수식자 없음

 

< 보호 스코프 >

: private[X], protected[X] 형식

: X라는 지정자를 통해 접근이 X까지 비공개이거나 보호

: X는 패키지, 클래스, 싱글톤 객체

 

package bobsrockets

package navigation {
   private[bobsrockets] class Navigator {       // 1
      protected[navigation] def useStarChart() = {}    // 4
   

       class LegOfJourney {
          private[Navigator] val distance = 100   // 2
       }
      private[this] var speed = 200   // 5
   }
}

 

package launch {
   import navigation._
   object Vehicle {
         private[launch] val guide = new Navigator    // 3
   }
}

1. Navigator은 bobsrockets 패키지에 있는 모든 객체와 클래스에서 접근 가능 => Vehicle 객체 내부에서 접근 가능

2. distance는 Navigator 클래스 내부 어디서나 접근 가능 == 자바 내부 클래스의 비공개 멤버와 동일한 접근 제어

3. guide 는 lanuch 패키지 내부 어디서든 접근 가능

4. useStarChart는 Navigator의 모든 서브 클래스 내부와 navigation 패키지에 있는 모든 객체, 클래스에서 접근 가능

5. private[this](객체 비공개)는 그 정의를 포함하는 객체 내부에서만 접근 가능, private보다 제한

 

class Foo {

    private[this] def isFoo = true

    def doFoo(other: Foo)  {

            if (other.isFoo) { // this line won't compile //

                ...

            }

         }

}

class Foo {

   private def isFoo = true

   def doFoo(other: Foo) {

       if (other.isFoo) { // this now compiles //

              ...

        }

    }

}

=> 같은 클래스의 다른 객체에서 접근하지 않음을 보장

 

 

< 가시성과 동반 객체 >

: 객체는 자신의 동반 클래스와 모든 접근 권리를 공유, 역도 마찬가지(클래스가 동반 객체의 비공개 멤버에 모두 접근할 수 있는 것처럼 객체도 동반 클래스의 모든 비공개 멤버에 접근할 수 있다.)

class Rocket {
    import Rocket.fuel
    private def canGoHomeAgain = fuel > 20
}


object Rocket {
    private def fuel = 10
    def chooseStrategy(rocket: Rocket) = {
        if (rocket.canGoHomeAgain)
             goHome()
        else
             pickAStar()
    }
    def goHome() = {}
    def pickAStar() = {}
}

: 싱글톤 객체는 서브 클래스를 만들 수가 없으므로 동반 객체 안에서 보호 멤버를 선언 X

 

 

< 패키지 객체 >

: 패키지 객체를 통해 패키지 내부 최상위 수준에 메소드를 정의할 수 있다. (global 메소드를 만들겠다.)

: 패키지 객체 내부에 있는 모든 정의는 패키지 자체에 속한 멤버로 취급

// In file bobsdelights/package.scala 
package object bobsdelights {
   def showFruit(fruit: Fruit) = {
      import fruit._
      println(name + "s are " + color)
   }
}


// In file PrintMenu.scala
package printmenu
import bobsdelights.Fruits
import bobsdelights.showFruit

 

object PrintMenu {
   def main(args: Array[String]) = {
          for (fruit <- Fruits.menu) {
              showFruit(fruit)
          }
    }
}

: 패키지 내에서 사용할 타입 별명과 암시적 변환을 넣기 위해 패키지 객체 사용(20장, 21장)

: 패키지 객체는 package.class 라는 클래스 이름으로 컴파일

: 컴파일된 클래스 파일은 패키지 클래스와 대응하는 패키지 디렉토리에 들어간다. 이 관례를 소스 파일에도 적용하는 것이 좋다.(bobsdelights 패키지 객체의 소스코드를 bobsdelights 디렉토리에 있는 package.scala로 저장)

+ Recent posts