Как вынести похожие сущности в один абстрактный класс?
Нужно создать интерфейс с методами sendSms() и getBalance(), затем реализовать его для каждого провайдера. Количество классов будет соответствовать количеству провайдеров, у всех будет один общий интерфейс.
Быстрый вариантclass Sms
{
    protected static $instances = [];

    public static function getInstance($id = 'default')
    {
        $map = [
            'first' => 'FirstSmsHandler',
            'second' => 'SecondSmsHandler',

            'default' => 'FirstSmsHandler',
        ];

        if (!isset(self::$instances[$id])) {
            if (!isset($map[$id])) {
                throw new \Exception(sprintf('Unknown hadler `%s`', $id));
            }

            $handler = new $map[$id];

            self::$instances[$id] = new self($handler);
        }

        return self::$instances[$id];
    }

    protected $handler;

    public function __construct(SmsHandler $handler)
    {
        $this->setHandler($handler)
    }

    public function setHandler(SmsHandler $handler)
    {
        return $this->handler = $handler;
    }

    public function changeHandlerByName($handlerName)
    {
        $map = [
            'first' => 'FirstSmsHandler',
            'second' => 'SecondSmsHandler',

            'default' => 'FirstSmsHandler',
        ];

        if (!isset($map[$handlerName])) {
            throw new \Exception(sprintf('Unknown handler `%s`', $handlerName));
        }

        $handler = new $map[$handlerName];

        $this->setHandler($handler);

        return $this;
    }

    public function __call($name, $arguments)
    {
        return call_user_func_array([$this->handler, $name], $arguments);
    }
}

$sms = new Sms(new FirstSmsHandler());
$sms->changeHandlerByName($_POST['provider']);

$sms = Sms::getInstance($_POST['provider']);

$sms->getBalance();
Можно еще почитать как ларавел реализовывает контракты, сервис контайнеры и сервис провайдеры. Там через конфиг просто меняем контейнер и уже работаем с другим классом. Почитайте.