2023. 1. 9. 13:22ใJAVA/Effective JAVA
์์ดํ 3. private ์์ฑ์๋ ์ด๊ฑฐ ํ์ ์ผ๋ก ์ฑ๊ธํค์์ ๋ณด์ฆํ๋ผ.
์ฑ๊ธํค singleton ์ด๋?
: ์ธ์คํด์ค๋ฅผ ์ค์ง ํ๋๋ง ์์ฑํ ์ ์๋ ํด๋์ค๋ฅผ ์๋ฏธํ๋ค.
์ฑ๊ธํค์ ์๋ก๋ ์ค๊ณ์ ์ ์ผํด์ผ ํ๋ ์์คํ ์ปดํฌ๋ํธ๋ฅผ ๋ค ์ ์๋ค.
๊ฒ์์์ ์ธ์ดํฉ ๊ฐ์ ๊ฒ์ด ์๋ค. (๋ณดํต ์ธ์ด๋ฅผ '์์ด'๋ก ํ๋ค ํ๋ฉด ์ด๋ค ์ค์ ์์๋ ํ๊ตญ์ด์ด๊ณ , ์ด๋ค ์ค์ ์์๋ ์ผ๋ณธ์ด์ด๊ณ ํ๋ฉด ์๋๋๊น ํ๋์ ์ธ์ด๋ก ์ ์งํ๋ค.)
๊ทธ๋ฐ๋ฐ ์ฑ๊ธํค์ผ๋ก ํด๋์ค๋ฅผ ๋ง๋ค๋ฉด ์ด๋ฅผ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ๋ฅผ ํ ์คํธํ๊ธฐ๊ฐ ์ด๋ ค์์ง ์ ์๋ค.
์ฑ๊ธํค ํด๋์ค ์ค์์๋ ์ธํฐํ์ด์ค๋ฅผ ์ ์ํ ๋ค์ ์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํด์ ๋ง๋ ํด๋์ค๊ฐ ์๋๋ผ๋ฉด ์ฑ๊ธํค ์ธ์คํด์ค๋ฅผ ๊ฐ์ง mock ๊ตฌํ์ผ๋ก ๋์ฒดํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค...
์ฑ๊ธํค singleton ๋ง๋๋ ๋ฐฉ๋ฒ 1
: private ์์ฑ์ + public static final ํ๋
๋จผ์ ์์ฑ์๋ private๋ก ์จ๊ธฐ๊ณ , ์ ์ผํ ์ธ์คํด์ค์ ์ ๊ทผํ ์ ์๋ ์๋จ์ public static final ๋ฐฉ์์ผ๋ก ์ ๊ณตํ๋ ๊ฒ์ด๋ค.
public class Elvis
{
// ์ฑ๊ธํค ์ค๋ธ์ ํธ
public static final Elvis INSTANCE = new Elvis();
private Elvis(){}
public void leaveTheBuilding()
{
System.out.println("Whoa baby, I'm outta here!");
}
public void sing()
{
System.out.println("I'll have a blue ~ Christmas without you~");
}
}
private ์์ฑ์๋ public static final ํ๋์ธ Elvis.INSTANCE๋ฅผ ์ด๊ธฐํํ ๋ ๋ฑ ํ ๋ฒ๋ง ํธ์ถ๋๋ค.
public์ด๋ protected ์์ฑ์๊ฐ ์๊ธฐ ๋๋ฌธ์ Elvis ํด๋์ค๊ฐ ์ด๊ธฐํ ๋ ๋ ๋ง๋ค์ด์ง ์ธ์คํด์ค๊ฐ ์ ์ฒด ์์คํ ์์ ํ๋๋ฟ์์ด ๋ณด์ฅ๋๋ค.
๊ทผ๋ฐ ๋ค๋ฅธ ์ธ์คํด์ค๊ฐ ๋ง๋ค์ด์ง ์ ์๋ ๋๊ฐ์ง์ ์์ธ๊ฐ ์กด์ฌํ๋ค.
๊ถํ์ด ์๋ ํด๋ผ์ด์ธํธ๋ ๋ฆฌํ๋ ์ API์ธ AccessibleObject.setAccessible ์ ์ฌ์ฉํด private ์์ฑ์๋ฅผ ํธ์ถํ ์ ์๋ค.
public static void main(String[] args)
{
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
// ์์ฑ์๋ก ์ฌ๋ฌ ์ธ์คํด์ค ๋ง๋ค๊ธฐ
// ๋ฆฌํ๋ ์
์ ์ฌ์ฉํ๋ฉด ๋ค๋ฅธ ์ธ์คํด์ค๋ฅผ ๋ง๋ค ์ ์๊ฒ ๋๋ค..!!!
try
{
Constructor<Elvis> defaultConstructor = Elvis.class.getDeclaredConstructor();
defaultConstructor.setAccessible(true); // true ๋ก ์ํ๋ฉด Elvis๋ ์์ฑ์๊ฐ private์ฌ์ ํธ์ถํ ์ ์๊ฒ๋จ
Elvis elvis1 = defaultConstructor.newInstance();
Elvis elvis2 = defaultConstructor.newInstance();
System.out.println("(elvis1 == elvis2) = " + (elvis1 == elvis2));
}
catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e)
{
throw new RuntimeException(e);
}
}
์ด๋ ๊ฒ ๋ฆฌํ๋ ์ ์์ setAccessible์ true๋ก ์ค์ ํ๋ฉด privae ์์ฑ์๋ฅผ ํธ์ถํ ์ ์๋ค.
์ด๋ฐ ๊ณต๊ฒฉ์ ๋ง๊ธฐ ์ํด์๋ ์์ฑ์๋ฅผ ์์ ํด์ ๋๋ฒ์งธ ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ ค ํ ๋ ์์ธ๋ฅผ ๋์ง๊ฒ ํ๋ฉด ๋๋ค.
public class Elvis implements Serializable
{
// ์ฑ๊ธํค ์ค๋ธ์ ํธ
public static final Elvis INSTANCE = new Elvis();
private static boolean created;
private Elvis()
{
if (created) {
// ๋ฆฌํ๋ ์
๊ณต๊ฒฉ์ ๋ฐฉ์ดํ๋ ค๋ฉด ์์ฑ์๋ฅผ ์์ ํ์ฌ ๋ ๋ฒ์งธ ๊ฐ์ฒด๊ฐ ์์ฑ๋๋ ค ํ ๋ ์์ธ๋ฅผ ๋์ง๊ฒ ํ๋ฉด ๋๋ค.
throw new UnsupportedOperationException("can't be created by constructor");
}
created = true;
}
public void leaveTheBuilding()
{
System.out.println("Whoa baby, I'm outta here!");
}
public void sing()
{
System.out.println("I'll have a blue ~ Christmas without you~");
}
// ์ฑ๊ธํค์์ ๋ณด์ฅํด์ฃผ๋ readResolve ๋ฉ์๋
// ์ญ์ง๋ ฌํ๋ฅผ ํ ๋ readResolve๋ผ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋๋ฐ ์ฌ๊ธฐ๋ฅผ ์ฑ๊ธํค์ผ๋ก ์์ฑํ INSTANCE๋ฅผ ๋ฐํํด์ฃผ๋ฉด
// ์ญ์ง๋ ฌํ ํ ๋ ์๋ก์ด ์ธ์คํด์ค๋ฅผ ์์ฑํ์ง ์๋๋ค.
private Object readResolve()
{
return INSTANCE;
}
}
created flag๋ฅผ ๋์ด์ ์ด๋ฏธ ์์ฑ๋์๋ค๋ฉด true๋ฅผ ๋ฐํํด์ true์ด๋ฉด UnsupportedOperationException์ ๋ฐ์์ํค๋๋ก ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ฑ๊ธํด ํด๋์ค๋ฅผ ์ง๋ ฌํํ๋ ค๋ฉด Serializable๋ง ๊ตฌํํ๋๊ฒ ๋์ด ์๋๋ผ readResolve ๋ฉ์๋๋ฅผ ์ ๊ณตํด์ผ ํ๋ค.
์๋ํ๋ฉด ์ญ์ง๋ ฌํํ ๋ readResolve๋ผ๋ ๋ฉ์๋๋ฅผ ์ฐธ์กฐํด์ ์๋ก์ด ์ธ์คํด์ค๊ฐ ์์ฑ๋๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์ ์์ ์ฝ๋์ฒ๋ผ readResolve๋ฐํ ๊ฐ์ ํด๋น ์ธ์คํด์ค๋ก ๋ฐํํด์คฌ๋ค.
public static void main(String[] args)
{
// ์ญ์ง๋ ฌํ๋ก ์ฌ๋ฌ ์ธ์คํด์ค ๋ง๋ค๊ธฐ
// ์ ์ฅํ๋ ์ฝ๋
try (ObjectOutput output = new ObjectOutputStream(new FileOutputStream("elvis.obj")))
{
output.writeObject(Elvis.INSTANCE);
}
catch (IOException e)
{
e.printStackTrace();
}
// ์ฝ์ด์ค๋ ์ฝ๋ (์ ์ฅ์ ํ๋ค๊ฐ ์ฝ์ด์ฌ ๋ ์๋ก์ด ์ธ์คํด์ค๊ฐ ์์ฑ๋๋ค.)
try (ObjectInput in = new ObjectInputStream(new FileInputStream("elvis.obj")))
{
Object elvis3 = in.readObject();
System.out.println("(elvis3 == Elvis.INSTANCE) = " + (elvis3 == Elvis.INSTANCE)); // false
}
catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
readResolve() ๋ฉ์๋๋ฅผ ๊ตฌํํด์ฃผ๊ณ ๋๋ฉด (elvis3 == Elvis.INSTANCE) = true๊ฐ ๋์จ๋ค.
๐ข private ์์ฑ์ + public static final ํ๋ ๋ฐฉ๋ฒ ์ฅ๋จ์
์ฅ์
: ๊ฐ๊ฒฐํ๊ณ ์ฑ๊ธํค์์ API์ ๋ค์ด๋ผ ์์๋ค.
๋จ์
: ์ฑ๊ธํค์ ์ฌ์ฉํ๋ ํด๋ผ์ด์ธํธ ํ ์คํธํ๊ธฐ ์ด๋ ค์์ง๋ค.
๋ฆฌํ๋ ์ ์ผ๋ก private ์์ฑ์๋ฅผ ํธ์ถํ ์ ์๋ค.
์ญ์ง๋ ฌํ ํ ๋ ์๋ก์ด ์ธ์คํด์ค๊ฐ ์๊ธธ ์ ์๋ค.