• Clean Recycler Adapter. Часть 1
    0
    По-моему, второй вариант смотрится тоже не особо красиво


    Ребят, ну надо понимать что в статье используется максимально упрощенная модель. Все описано в одном классе, используется enum и т.д. Это сделано умышленно для упрощения восприятия идеи. Как вы реализуете эту идею — уже другой вопрос.

    Вот как выглядит моя боевая реализация:

    Вариант боевой реализации идеи
    public class UsersArbitraryCellAdapter extends ArbitraryCellAdapter {
    
        public UsersArbitraryCellAdapter() {
            this.arbitraryCellSelector.addCell(new ProgressArbitraryCell());
            this.arbitraryCellSelector.addCell(new AdArbitraryCell());
            this.arbitraryCellSelector.addCell(new UserArbitraryCell());
        }
    
        public void setUsers(List<UserVo> userList) {
    		// Set users, ads, progress...
        }
    }
    
    public abstract class ArbitraryCellAdapter
    		extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        protected ArbitraryCellSelector arbitraryCellSelector = new ArbitraryCellSelector();
        protected List itemList = new ArrayList();
    
        @Override
        public final int getItemViewType(int position) {
            return arbitraryCellSelector.getCell(itemList.get(position)).type();
        }
    
        @Override
        public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            return arbitraryCellSelector.getCell(viewType).holder(parent);
        }
    
        @Override
        public final void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            Object item = itemList.get(position);
            arbitraryCellSelector.getCell(item).bind(holder, item);
        }
    
        @Override
        public final int getItemCount() {
            return itemList.size();
        }
    }
    
    public abstract class ArbitraryCellHolder<T> extends RecyclerView.ViewHolder {
    
        public ArbitraryCellHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    
        public abstract void bind(T item);
    }
    
    public final class ArbitraryCellSelector {
        private List<Cell> cellList = new ArrayList<>();
    
        public void addCell(Cell cell) {
            cellList.add(cell);
        }
    
        public void removeCell(Cell cell) {
            cellList.remove(cell);
        }
    
        public Cell getCell(Object item) {
            for (Cell cell : cellList) {
                if (cell.is(item)) {
                    return cell;
                }
            }
            throw new NoSuchRecyclerRowException();
        }
    
        public Cell getCell(int viewType) {
            for (Cell cell : cellList) {
                if (cell.type() == viewType) {
                    return cell;
                }
            }
            throw new NoSuchRecyclerRowException();
        }
    
        public interface Cell {
    
            boolean is(Object item);
    
            int type();
    
            RecyclerView.ViewHolder holder(ViewGroup parent);
    
            void bind(RecyclerView.ViewHolder holder, Object item);
        }
    }
    
    public class AdArbitraryCell implements ArbitraryCellSelector.Cell {
    
        @Override
        public boolean is(Object item) {
            return item instanceof AdVo;
        }
    
        @Override
        public int type() {
            return R.layout.cell_ad;
        }
    
        @Override
        public RecyclerView.ViewHolder holder(ViewGroup parent) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            View view = inflater.inflate(R.layout.cell_ad, parent, false);
            return new AdViewHolder(view);
        }
    
        @Override
        public void bind(RecyclerView.ViewHolder holder, Object item) {
            try {
                AdViewHolder adViewHolder = (AdViewHolder) holder;
                AdVo ad = (AdVo) item;
                adViewHolder.bind(ad);
            } catch (ClassCastException e) {
                L.printStackTrace(e);
            }
        }
    
        protected class AdViewHolder extends ArbitraryCellHolder<AdVo> {
            @BindView(R.id.ad_text_view)
            protected TextView adTextView;
    
            public AdViewHolder(View itemView) {
                super(itemView);
            }
    
            @Override
            public void bind(AdVo item) {
                adTextView.setText(item.getTitle());
    
                itemView.setOnClickListener(view -> adPublishSubject.onNext(item));
            }
        }
    }
    
    // Other ArbitraryCells...
    



    Про Adapter Delegates автор не слышал?


    Действительно не слышал. Как и многие другие, исходя из моего немалого опыта и оценки статьи сообществом.

    Решение изящное, признаю. Что использовать у себя в проекте — импортировать библиотеку или написать один вспомогательный класс ArbitraryCellSelector — дело личного каждого.
  • Clean Recycler Adapter. Часть 1
    0
    Достойно :)
  • Clean Recycler Adapter. Часть 1
    0
    Еще я бы избавился от неявных зависимостей и вынес весь класс CellType наружу.


    В боевых условиях именно так и делается :)

    Цель статьи — максимально просто и доступно донести идею. А как вы это реализуете под себя — вопрос десятый.
  • Clean Recycler Adapter. Часть 1
    +1
    В идеале от класса CellType нужно избавляться. Точнее заменять его на не статичный объект. Не секрет, что enum в Java является статичным объектом, а статика может в определенных ситуациях стать головной болью и причиной падений.

    В статье enum использован для упрощения материала и простоты восприятия идеи. С этой же цель явно прописаны все методы в контракте enum и локальные переменные.
  • Clean Recycler Adapter. Часть 1
    0
    Все правильно, именно такой подход позволяет избавиться от проверок на тип и преобразований типов. И это самый очевидный подход для решения этой задачи.

    Но как вы правильно заметили, у этого подхода есть один недостаток. Под каждый адаптер нам потребуется столько классов-оберток, сколько разнотипных ячеек планируется использовать. Плюс под каждый такой адаптер (а точнее для классов-оберток под текущий адаптер) в идеале создается еще и интерфейс.

    Плюс только лишь оборачивание не решает всех поставленных вопросов.
  • Clean Recycler Adapter. Часть 1
    0
    Я сторонник «тонких» моделей. Т.е. модель здесь понимается в самом узком смысле — это просто DataObject. И вся его обязанность сводится к тому, чтобы предоставлять нам доступ к данным. Если обязать этот объект заниматься обработкой самого себя, то это уже будет нарушением принципа SRP.

    Но такой подход возможен. И даже может позволить избежать проверок на тип и приведений типов.