Mixin


Mixin

이펙티브의 클래스와 인터페이스 부분을 읽다가 mixin이란 개념이 나왔는데

믹스인이란 클래스가 구현할 수 있는 타입으로 믹스인을 구현한 클래스에         
원래의 '주된 타입' 외에도 특정 선택적 행위를 제공한다고 선언하는 효과를 준다.           
예) Comparable은 자신을 구현한 클래스의 인스턴스들끼리는 순서를 정할 수 있다고 선언하는 믹스인 인터페이스이다.       
이처럼 대상 타입의 주된 기능에 선택적 기능을 '혼합(mixed-in)'한다고 해서 믹스인이라고 부릅니다. 

위 설명만으로 부족해 mixin에 대해 찾아봤는데
스택 오버플로에 나와 같은 고민으로 글을 작성한 사람도 있었다.
https://stackoverflow.com/questions/17987704/an-example-of-a-mixin-in-java

Using mixins in Java

클래스에 기능을 덧붙여야 할 때가 있는데 대부분 Mixin보다는 Delegator로 구현한다.
이번에는 일급 컬렉션(first class collections)을 사용하여 Java에서 Mixin을 구현하는 방법을 알아보자.

일급 컬렉션에 대해
https://woowacourse.github.io/javable/post/2020-05-08-First-Class-Collection/
https://jojoldu.tistory.com/412


exam1. 구독자 목록을 포함한 Magazine.java, Newspaper.java

일반적으로 다음과 같이 모델링한다.

// Magazine.java
class Magazine {
    private List<Subcriber> subscribers;
    
    public void addSub(Subscriber subscriber) {
        subscribers.add(subscriber);
    }
    
    public List<Subcriber> primeSubscribers() {
        . . .
    }
}

// Newspaper.java   
class Newspaper {
    private DateTime date; 
    private List<Subcriber> subscribers; 
    
    public void addSub(Subscriber subscriber) {
        subscribers.add(subscriber);
    }
    
    public List<Subcriber> primeSubscribers() {
        . . .
    }
}

구독자 목록 관련된 작업이 두 클래스에서 모두 중복된다.
구독자 목록인 List<Subcriber> subscribers
필드로 갖고 있는 다른 클래스를 만들면 이를 해결할 수 있다.

class Subcribers {
    private List<Subcriber> subscribers; 
}


List와 Subscribers을 별도의 클래스로 분리

custom arrayList에서 동작하게 하려면 List를 재정의해야 한다.

interface ListMixin<T> extends List<T> {
    List<T> getRecords();
    
    default int size() {
        return getRecords().size();
    }
    
    . . .
    
}


Subscribers에서 ListMixin 인터페이스를 구현하고
getRecords() 메서드를 재정의하여 구독자 목록을 반환함으로써
이 클래스를 List처럼 사용할 수 있다.
구독자 리스트에 관련된 작업이 전부 이 클래스에 들어간다.

class Subcribers implements ListMixin<Subcriber> {
    private List<Subcriber> subcribers = new ArrayList<>();
    
    @Override 
    public List<Subcriber> getRecords() {
        return subcribers;
    }
    
    public void addSub(Subscriber subscriber) {
        subscribers.add(subscriber);
    }
    
    . . .
    
}


이제 Magazine.java, Newspaper.java에서
구독자 명단을 반환하는 로직을 다루지 않아도 된다.

class Magazine {

    private Subcribers Subcribers;
    
    . . .
    
}


exam2. mixin exam code

interface TimeStamped {
	long getStamp();
}


class TimeStampedImp implements TimeStamped {
	private final long timeStamp;

	public TimeStampedImp() {
		timeStamp = new Date().getTime();
	}

	public long getStamp() {
		return timeStamp;
	}
}


interface SerialNumbered {
	long getSerialNumber();
}


class SerialNumberedImp implements SerialNumbered {
	private static long counter = 1;
	private final long serialNumber = counter++;

	public long getSerialNumber() {
		return serialNumber;
	}
}


class Basic {
	private String value;

	public void set(String val) {
		value = val;
	}

	public String get() {
		return value;
	}
}


class Mixin extends Basic implements TimeStamped, SerialNumbered {
	private TimeStamped timeStamp = new TimeStampedImp();
	private SerialNumbered serialNumber = new SerialNumberedImp();

	public long getStamp() {
		return timeStamp.getStamp();
	}

	public long getSerialNumber() {
		return serialNumber.getSerialNumber();
	}
}


public class Mixins {
	public static void main(String[] args) {
		Mixin mixin1 = new Mixin();
		Mixin mixin2 = new Mixin();

		mixin1.set("test string 1");
		mixin2.set("test string 2");

		System.out.println(mixin1.get() + " " + mixin1.getStamp() + " " + mixin1.getSerialNumber());
		System.out.println(mixin2.get() + " " + mixin2.getStamp() + " " + mixin2.getSerialNumber());
	}
}


KakaoTalk_Photo_2021-06-22-00-08-43