mercredi 10 juin 2020

Is this a valid implementation of state pattern?

The Article model supports a bunch of transitions. Only two are shown below: star()/unstar().

interface ArticleState {
  star(): ArticleState;
  unstar(): ArticleState;
  // other transitions ...
}

export class InitialArticleState implements ArticleState {
  star   = () => new StarredArticleState();
  unstar = () => this;
  // other transitions ...
}

export class StarredArticleState implements ArticleState {
  star   = () => this;
  unstar = () => new InitialArticleState();
  // other transitions ...
}

export class Article {  
  state: ArticleState = new InitialArticleState();  
  star() { this.state = this.state.star(); }
  unstar() { this.state = this.state.unstar(); }
}

The Article state operations are wrapped by the service which updates the feed:

@Injectable()
export class ArticlesFeedService {

  private feed$: Subject<Article[]> = new BehaviorSubject<Article[]>([]);
  private store: Article[] = [];

  private publishChanges() {
    this.feed$.next(this.store);
  }

  starArticle(article: Article): void {
    article.star();
    // update the store then publish changes...
  }
}

There's a component subscribed to get starred articles:

getStarredArticles(): Observable<Article[]> {
  return this.getArticles().pipe(
    map((articles: Article[]) => 
      articles.filter((article: Article) => 
        article.state instanceof StarredArticleState
      )
    )
  );
}

My motivation behind using the state pattern is that it helps reduce branching (decision making) in the feed service methods (e.g. starred article can't be blacklisted, only unstarred / initial).

  1. Is it okay to use state as public property of the Article?
  2. How can I improve the design?
  3. Are there any TypeScript features that could be used instead?

Aucun commentaire:

Enregistrer un commentaire