**Description**

Currently `BaseOperator` and `XComArgs` implements the same logic of `__lshift__`, `__rshift__` and others "chain" operations (`>>`, `<<`). It seems resonable to abstract this logic into `TaskMixin` to introduce some DRYness especially because the new concept of `TaskGroup` will also implement the same methods.

**Use case / motivation**

Limit duplication of logic.

**Related Issues**

https://github.com/apache/airflow/pull/10827#issuecomment-691927501
#10153 

airflow/models/baseoperator.py
self,
            start_date: Optional[datetime] = None,
            end_date: Optional[datetime] = None,
            ignore_first_depends_on_past: bool = True,
            ignore_ti_state: bool = False,
            mark_success: bool = False) -> None:
        """
        Run a set of task instances for a date range.
        """
        start_date = start_date or self.start_date
        end_date = end_date or self.end_date or timezone.utcnow()
        for execution_date in self.dag.date_range(start_date, end_date=end_date):
            TaskInstance(self, execution_date).run(
                mark_success=mark_success,
                ignore_depends_on_past=(
                    execution_date == start_date and ignore_first_depends_on_past),
                ignore_ti_state=ignore_ti_state)
    def dry_run(self) -> None:
        """Performs dry run for the operator - just render template fields."""
        self.log.info('Dry run')
        for field in self.template_fields:
            content = getattr(self, field)
            if content and isinstance(content, str):
                self.log.info('Rendering template for %s', field)
                self.log.info(content)
    def get_direct_relative_ids(self, upstream: bool = False) -> Set[str]: