[요약]
● 크기가 작고 상수이거나 변하지 않는 데이터 셋은 Observable이 적합하지 않습니다. ● 정규표현식 필드를 사용한다면 인스턴스를 만드는 것에 높은 비용을 요구하기때문에 적합하지 않습니다. ● 간단한 ‘찾기’나 한 단계 조작만이 필요한 경우 굳이 Observable을 만들 필요 없이 더 깔끔한 코드를 구성하는 편이 좋습니다. ● 빈번하게 필터링(filtered/qualified)되는 필드라면 RxJava를 사용하지 않는 편이 더 좋습니다. |
리 액티브 프로그래밍은 게임을 바꾸는 기술입니다. 올바르게 사용한다면 프로그래밍 방식에 변화가 있어야합니다.
1 년 전, UI 이벤트를 관리하고 작성하는 더 나은 방법을 찾고 (결과적으로 RxJavaFX의 소유권을 얻었 으면 ) 연구하기를 바랬습니다 .
그러나 나는 그것이 그것 보다 훨씬 더 많은 것을 성취한다는 것을 빨리 배웠다.
동시성 및 IO에서부터 로직 및 알고리즘에 이르는 프로그래밍의 거의 모든 측면에 대한 접근 방식을 변경합니다.
내 열정으로 RxJava를 사용하기 시작했다. 반응 형 프로그래밍에 대한 이러한 획기적인 접근 방법을 사용하면 응용 프로그램의 품질이 크게 향상되었습니다. 나에게 모든 것을 반응 적으로 만드는 것이 아마 RxJava를 배우는 가장 효과적인 방법이었을 것이다.
그러나 1 년이 지난 후에, 나는 반작용이 꼭 맞는 것은 아닐 수도있는 몇 가지 사례를 발견했다. 지금 쓰는 모든 신청서는 사후 처리됩니다.
그러나 전략적으로 반응을 보이지 않기로 선택한 코드베이스에는 몇 군데가있을 수 있습니다.
이것은 부분적으로 Kotlin으로의 전환으로 인한 것일 수 있습니다.
Kotlin 은 푸시 기반이든 푸시 기반이든 상관없이 기능 프로그래밍을 편리하게 만들어 Rx를 내 벨트에 몇 가지 기능적 툴 중 하나만 만들었습니다.
그러나 나는 빗나간다. RxJava를 사용하는 Observable
것이 최적이 아닌 경우 이 기사는 단지 내 관찰일 뿐 입니다.
Observable
콜렉션을 포함하여 무엇이든 쉽게 전환 할 수 있다는 것을 명심하십시오.
따라서 이 게시물은 Observable
항목이 API에서 반환되지 않도록 하는 것이 적절할 때 입니다.
그들이 선택한 경우 API 클라이언트는 Observables로 항상이 항목을 전환 할 수 있습니다.
사례 # 1 작고 일정하며 변함없는 데이터 세트
다음은 an Observable
이 적절하지 않을 수 있는 가장 간단한 경우 입니다.
enum
세 가지 EmployeeType
카테고리에 대한 간단한 유형 이 있다고 가정 해 보겠습니다 .
public enum EmployeeType {
FULL_TIME,
CONTRACTOR,
INTERN
}
이 열거 형을 반복 할 필요가 있다면, 항상 그것을 열거 형으로 바꾸는 것이 이치에 Observable
맞습니까?
Observable<EmployeeType> employeeTypes = Observable.from(Employee.values());
아마 당신이 사업자 사슬 에서 이미 깊숙한 곳 EmployeeType
으로 진입 Observable
했다면 그것은 아마도 의미가 있을지도 모릅니다. Observable
단순히 푸시 기반의 흐름을 따르기 만하면됩니다 Observable
. 그러나 나는 이것이 규범 이라기보다는 예외라고 주장 할 것이다.
작고 변경되지 않는 데이터 세트는 아마도 좋은 후보로 전환 할 수 없습니다 Observable
. API 관점에서 전통적인 컬렉션으로 남겨 두었다가 Observable
의미 있는 시점 으로 변환되도록 허용합니다 . 이것은 단지 열거 형에 적용되지 않으며 아마도 이것은 극단적 인 예입니다. 그러나 작고 정적 인 데이터 세트는이 경우에 빠질 수 있습니다.
사례 2 비용이 많이 드는 캐시 된 개체
비싼 정규식 필드 가있는 클래스가 있다고 가정 해 보겠습니다 . 당신이 모를 경우에, 정규 표현식 은 스테로이드의 텍스트 와일드 카드입니다. 텍스트 패턴을 찾는 데는 매우 강력하지만 런타임에 컴파일하는 데 비용이 많이 듭니다. 따라서이 인스턴스를 생성하면
ActionQualifierActionQualifier
중복 작업을 수행하는 경우 성능이 매우 높아질 수 있습니다.
public final class ActionQualifier {
private final Pattern codeRegexPattern;
private final int actionNumber;
ActionQualifier(String codeRegex, int actionNumber) {
this.codeRegexPattern = Pattern.compile(codeRegex);
this.actionNumber = actionNumber;
}
public boolean qualify(String code) {
return codeRegexPattern.matcher(code).find();
}
public int getActionCode() {
return actionNumber;
}
}
당신이이 있다면 Observable
수입이 ActionQualifier
사용하여 데이터베이스에서 인스턴스를 RxJava - JDBC를 은 (재 쿼리의 모든 가입 결과 이후) 여러 가입자가있는 경우, 그것은 매우 비싼 수 있습니다. 모든 구독마다 매번 다시 구독합니다.
Observable<ActionQualifier> actionQualifiers = db
.select("SELECT CODE_REGEX, ACTION_NUMBER FROM ACTION_MAPPING")
.get(rs -> new ActionQualifier(rs.getString("CODE_REGEX"), rs.getInt("ACTION_NUMBER")));
cache()
연산자를 사용하여 해당 연산자를 잡고 각 구독자에게 "재생"할 수 있으며 이는 유효합니다. 그러나 무기한으로 무기력하게 유지되는 actionQualifiers
것처럼 오래 가지 못할 수 있습니다 cache()
.
Observable<ActionQualifier> actionQualifiers = db
.select("SELECT CODE_REGEX, ACTION_NUMBER FROM ACTION_MAPPING")
.get(rs -> new ActionQualifier(rs.getString("CODE_REGEX"), rs.getInt("ACTION_NUMBER")))
.cache();
Dave Moten은 캐시를 만료하고 소스에 다시 구독 하는 똑똑한 솔루션 을 만들었습니다 . 그러나 궁극적으로 단순히 actionQualifiers
in을 누르고 더 쉽게 List
수동으로 새로 고칠 수 있는지 물어보아야합니다 . 당신은 더 이상 모나드에 구속되지 않을 수도 있습니다.
List<ActionQualifier> actionQualifiers = db
.select("SELECT CODE_REGEX, ACTION_NUMBER FROM ACTION_MAPPING")
.get(rs -> new ActionQualifier(rs.getString("CODE_REGEX"), rs.getInt("ACTION_NUMBER")))
.toList().toBlocking().first();
그런 다음 언제든지 저장 List
한 파일 Observable
을 언제든지 사용할 수 있습니다 Observable
.
Observable.from(actionQualifiers).filter(aq -> aq.qualify("TXB.*"));
어느 쪽이든 비싼 물건들을 모으는 것은 힘든 일이 될 수 있습니다. 일부 상황에서는 기능적으로 관리하는 것이 아니라 상태에 따라 관리하는 것이 더 쉽습니다. 당신은 아마도 당신의 상황에 따라 반응적인 성격을 유지할 수있는 창조적 인 방법을 찾지 만, 지나치게 복잡하지 않도록주의하십시오. 그러나 매우 큰 메모리 집약적 인 콜렉션을 가지고 있다면 Observable
캐싱이 메모리를 차지하지 못하게 하는 것이 유효 할 수도 있습니다. 정말로 "비싼"것이 무엇을 의미하는지에 달려 있습니다.
사례 # 3 간단한 "조회"및 단일 단계 Monads
RxJava의 장점은 여러 단계를 구성 할 수 있다는 것입니다. 이러한를 타고 다음, 그 필터링 , 이 매핑 하고, 그로 감소 . 작은 코드로 수 많은 작업을 수행하는 길고 정교한 작업 체인을 만들 수 있습니다.
Observable<Product> products = ...
Observable<Int> totalSoldUnits = products
.filter(pd -> pd.getCategoryId() == 3)
.map(pd -> pd.getSoldUnits())
.reduce(0,(x,y) -> x + y)
그러나 ID가있는 단일 값을 찾는 것과 같은 한 단계 만 수행하는 데 관심이 있다면 어떨까요?
Observable<Category> category = Category.forId(263);
이 경우 Observable
과잉 인가 ? 아마도. 를 Category
통해 방출되지 않고 를 반환하는 것이 더 간단 할 수도 있습니다 Observable
.
Category category = Category.forId(263);
어쩌면 Observable
하나 이상 발급 Category
될 것으로 예상되는 경우 an 은 보증 되며, 해당 ID가 없으면 값 Observable
대신 빈 null
값 을 반환하고자합니다 Category
. 하지만 Case # 4에서 다음과 같은 것을 발견하게되면 이러한 과도한 사용은 Observable
이를 줄이기보다는 더 많은 상용구 코드를 만들 수 있습니다. 그러나 지금은 단순히 "조회 (look up)"한 단일 값이 필요하거나 단 하나의 단계 만 필요하다고 생각한다면 메모를 사용하지 않는 것이 Observable
좋습니다.
게다가, 만약 당신이 정말로 Observable
주어진 용도에 대해 활용하고 싶다면 , 나중에 언제든지 그것을 하나로 변환 할 수 있습니다. null 값을 필터링하여 비워 둘 수 있습니다.
Observable<Category> category = Observable.just(Category.forId(263))
.filter(c -> c != null);
사례 4 자주 등록 된 속성
사례 # 3을 확장하여 다른 지점을 만들어 보겠습니다. 이 Product
수업을 듣고 있다고 해 .
public final class Product {
private final int id;
private final String description;
private final int categoryId;
public Product(int id, String description, int categoryId) {
this.id = id;
this.description = description;
this.categoryId = categoryId;
}
public Observable<Category> getCategory() {
return Category.forId(categoryId);
}
}
통지 getCategory()
메소드가 반환합니다 Observable<Category>
. 우리가 자주 자격을 얻는다면 Category
, 이것은 꽤 지저분 할 수 있습니다. 각각 Category
에 getGroup()
반환 값 이 있다고 가정 하고 카테고리 그룹이 5 인 제품 만 int
필터링하려고합니다 Observable<Product>
.
Observable<Product> products = ...
Observable<Product> productsInGroup5 =
products.flatMap(p -> p.getCategory().filter(c -> c.getGroup() == 5).map(p -> c));
간단한 작업을 위해서는 FlatMap Kung-Fu가 많이 필요합니다. 우리는 그것의 flatMap()
각각 Product
에 Category
, 그 때 getGroup()
5를 위한 종류를위한 여과기, 그 후에 Category
등을 맞댄 지도로 나타낸다 Product
. 우리가 가지고가는 경우에 Product
클래스를하고 만들 getCategory()
비 Observable
,이 훨씬 간단 할 것이다.
public Category getCategory() {
return Category.forId(categoryId);
}
Observable<Product> productsInGroup5 =
products.filter(p -> p.getCategory().getGroup() == 5);
자주에 / 자격을 필터링하는 클래스의 필드가있는 경우 요약하면, 당신은 고려할 수 없는 그것에게 만드는 Observable
복잡성을 피하기 위해. 속성이 값의 시퀀스가 아닌 단일 값을 반환하는 경우 특히 그렇습니다.
사례 # 5 상태 캡처
RxJava는 매우 반 국가입니다. 이것은 좋은 일입니다. 일련의 상태를 수동으로 관리하는 대신 일련의 동작과 동작을 구성 할 수 있습니다. 이를 통해 작업은 스레드에 대해 불가지론적일 수 있으며 언제든지 모든 Observable
체인에 동시성을 구성 할 수 있습니다 .
하지만 복잡한 비즈니스 응용 프로그램 (반응 형 프로그래밍의 이점을 확실히 누리고 있음)에 주목했습니다. 때때로 알고리즘의 작동 방식에 대한 컨텍스트를 수집하려고 할 때 상태를 캡처하려고 할 때가 있습니다. 이는 비즈니스보고에 중요합니다. 제가 생각할 수있는 가장 간단한 예는 역사의 스냅 샷을 붙잡고있는 것입니다.
public final class PricePoint {
private final int id;
private final int productId;
private final BigDecimal price;
private final ImmutableList<BigDecimal> historicalPricePoints;
public PricePoint(int id, int productId, BigDecimal price) {
this.id = id;
this.productId = productId;
this.price = price;
historicalPricePoints = HistoricalPricePoints.forProductId(productId);
}
public ImmutableList<BigDecimal> getHistoricalPricePoints() {
return historicalPricePoints;
}
}
역사적 가격 점을 반응 적으로 검색 할 수 있습니다. 조작이 비싸지 않은 경우에는이 점이 좋습니다.
public final class PricePoint {
private final int id;
private final int productId;
private final BigDecimal price;
public PricePoint(int id, int productId, BigDecimal price) {
this.id = id;
this.productId = productId;
this.price = price;
}
public Observable<BigDecimal> getHistoricalPricePoints() {
return HistoricalPricePoints.forProductId(productId);
}
}
그러나 비싸면 Case # 2로 돌아갑니다. 또한, 건설 당시 의 역사적 가격 점 을 포착 하려면PricePoint
an Observable
이 최적이 아닐 수도 있습니다.
당신은 실제로 주를 붙잡고 싶습니다. RxJava와 같은 반 국가적 해결책이 이것을 훼손 할 수도 있습니다.
컨텍스트 스냅 샷을 유지할 수 있도록 여러 속성에 대해 서로 다른 상태를 수집하고 유지하려는 경우 기존의 끌어 오기 기반 솔루션을 사용하여 모든 것을 구성 할 수 있습니다.
개요
리 액티브 프로그래밍은 분명히 게임 체인저이며, 당신은 그것을 자유롭게 사용해야합니다. 그러나 RxJava는 중급에서 고난이도 문제를 다루지 만 반드시 간단한 문제는 다루지 않습니다. 위에서 확인한 이러한 사례 중 일부는 Rx 참전 용사에게 분명 할 수 있습니다. 그러나 초보자에게 "기초부터 반응하는"응용 프로그램을 개발하도록 권장 했으므로 그렇게 명확하지 않을 수 있습니다.
다시 말하지만, 이것은 단지 나의 관찰 일뿐입니다. 질문, 추가 사례, 확인, 불일치 또는 의견이 있으면 아래에 의견을 말하십시오.