Ответы пользователя по тегу ООП
  • Как лучше реализовать архитектуру MVC/MVP?

    @MarkusD
    все время мелю чепуху :)
    Первым делом стоит обратиться к описанию[T] MVP от Мартина Фаулера.

    Фаулер сразу оперирует поверх GUI на базе модели форм и элементов, т.е. рассматривает твой конкретный случай.
    Модель форм и элементов оперирует событиями элементов для интерпретации пользовательского ввода. Согласно Фаулеру, обработку этих событий стоит передать презентеру. Презентер в этом случае обрабатывает пользовательский ввод, передает его модели и собирает с модели обновленные данные для передачи в вид. Внутренние данные вида, которые не предназначены для модели, должен обрабатывать тоже презентер, после чего все так же передавать в вид.
    Вид должен только отображать данные модели и отсылать в презентер сигналы пользовательского ввода.

    Что будет если сделать по-другому. Например, как описано у тебя в вопросе. Сейчас у себя ты потерял гибкость и буквально разрушил абстракцию презентера. Если презентер вдруг потребуется сделать композитным (например, выбирать видимые и доступные для ввода элементы вида), ты упрешься в рефакторинг и вида, и презентера. Это отсрочит реализацию композитности логики презентера. Если потребуется держать одновременно несколько видов для одной модели, тебе придется как-то внедрять Change Propagation. В рядовом MVP за это отвечает презентер, а у тебя выйдет что вид должен подписываться на уведомление, что характерно уже не для MVP, а для MVC.

    MVP, как и MVC, является архитектурным шаблоном. Такие шаблоны находятся на самом верхнем уровне пирамиды отношений шаблонов. Это говорит о том, что уже просто реализация MVC/MVP в лоб в коде является нежелательной. MVC/MVP задают для кода UI строгое разделение по функциональности: ввод данных, процессинг и вывод данных. Вот что должно явно присутствовать в твоем коде, вот что стоит реализовать с использованием шаблонов дизайна и идиом разработки. Например, презентер или контроллер может быть сформирован на базе Rx и быть полностью децентрализованным, но при этом качественно выполнять свои функции. А вид и вовсе может быть data-driven объектом, т.е. не иметь даже минимальной личной логики.
    Каждый архитектурный шаблон, помимо легкой поддержки, сформирован из расчета на изначальную простоту, возможность стыковки с другими архитектурными шаблонами и потенциальную расширяемость.
    В результате, заложив изначально слабую реализацию MVP, дальнейшими действиями ты рискуешь размыть границы элементов архитектуры, снизить прозрачность реализации для понимания другими людьми и усложнить поддержку этого кода.
    Ответ написан
  • Как сделать "переопределение" функции в c++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Твой вопрос можно решить таким способом.
    #include <iostream>
    #include <functional>
    
    class Enemy final
    {
    public:
    	std::function<void()> update = []() {};
    };
    
    int main( int argc, char* argv[] )
    {
    	Enemy enemy;
    	enemy.update = []() { std::cout << "Hello"; };
    
    	enemy.update();
    	return 0;
    }
    Ответ написан
  • Как реализовать наследование статического поля/метода, если это возможно?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    CodeInside , в твоем вопросе тебе поможет такой примитив проектирования, как Абстрактная Фабрика.

    Простая реализация на шаблоне и C++11 выглядит примерно так.
    spoiler
    template< typename TInterface, typename... TArguments >
    class AbstractFactory final
    {
    public:
    	// Produce the implementation, but return the pointer to interface.
    	inline std::shared_ptr<TInterface> Produce( const std::string& implementation_name, TArguments... arguments )
    	{
    		auto found_function = m_factory_functions.find( implementation_name );
    		return ( found_function == m_factory_functions.end() )? std::shared_ptr<TInterface>{} : found_function->second( std::forward<TArguments>( arguments )... );
    	};
    	
    	// Define the implementation.
    	template< typename TImplementation >
    	inline const bool DefineImplementation()
    	{
    		return DefineImplementation<TImplementation>( TImplementation::ClassName() );
    	};
    	
    	// Define the implementation.
    	template< typename TImplementation >
    	inline const bool DefineImplementation( const std::string& implementation_name )
    	{
    		// Abort the incorrect registration.
    		static_assert( std::is_base_of<TInterface, TImplementation>::value, "Implementation may only be derived from interface of Factory." );
    		
    		auto found_function = m_factory_functions.find( implementation_name );
    		if( found_function == m_factory_functions.end() )
    		{
    			m_factory_functions[ implementation_name ] = &AbstractFactory<TInterface, TArguments...>::template ConstructImplementation<TImplementation>;
    			return true;
    		};
    		
    		return false;
    	};
    	
    	// Check the implementation name is already defined.
    	inline const bool IsImplementationDefined( const std::string& implementation_name ) const
    	{
    		return m_factory_functions.find( implementation_name ) != m_factory_functions.end();
    	};
    	
    private:
    	// The factory function just produce implementation.
    	template< typename TImplementation >
    	static std::shared_ptr<TInterface> ConstructImplementation( TArguments... arguments )
    	{
    		return std::static_pointer_cast<TInterface>(
    			std::make_shared<TImplementation>( std::forward<TArguments>( arguments )... )
    		);
    	};
    
    private:
    	// Factory function produces the implementations of TInterface.
    	using FactoryFunction	= std::shared_ptr<TInterface> (*)( TArguments... arguments );
    	
    	std::unordered_map<std::string, FactoryFunction>	m_factory_functions;
    };


    Работает она примерно так:
    cpp.sh/93obm
    Ответ написан
  • Можно ли так инициировать компоненты класса?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    en.cppreference.com/w/cpp/language/class
    Параграф "Member specification".
    Если ты используешь стандарт c++11 и выше, то инициализация полей непреложна.

    Единственным исключением будет список инициализации полей в пользовательском конструкторе.
    en.cppreference.com/w/cpp/language/initializer_list
    Ответ написан
  • Как решить проблему с ООП?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Да вы, батенька, король контента!

    Эт шутка. Хоть и не смешная для самоделкиных из области геймдева. :)
    Так, а кроме шуток. Не надо вообще так делать. Тип наносимого урона/снаряда/юнита/орудия никогда не должен описываться его собственным классом. Все юниты должны быть одного класса, все пули - тоже, все здания - тоже.
    У тебя должен быть только набор общих классов для сущностей. Конкретные параметры (что откуда вылетает, как дамажит и как это все визуально выглядит) должны лежать в ресурсах.
    Рекомендую почитать: habrahabr.ru/post/255561 Изложенное куда более важно для области любимых самокатов на С++.

    Хотя... Если тебе так надо реализовать на классах, а не на ресурсах, то тебе надо просто организовать фактическую связь между сущностями. Давай предложу один страшный вариант... Дебажить это все чур самому.
    class TowerParent
    {
    public:
    	// Это тип для той штуки, которая будет создавать нам нужную боеголовку.
    	typedef FireParent* (*FireConstructor)( float, float, int, float, float, float );
    	
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    	
    	FireParent* towerFire;
    protected:
    	FireConstructor	fire_constructor; // инстанция конструктора боеголовок.
    	
    	// Вот так мы будем определять тип конструируемой боеголовки.
    	template< class fire_t >
    	inline void DefineFireType(){
    		fire_constructor = TowerParent::template MakeFire<fire_t>;
    	};
    	
    private:
    	// А вот так мы будем конструировать боеголовку.
    	template< class fire_t >
    	static FireParent* MakeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    
    SimpleTower( int _col, int _row, float _posX, float _posY, float _attackRange, float _angle ){
    	DefineFireType<Bullet>();
    };
    
    RocketTower(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle){
    	DefineFireType<Rocket>();
    };


    Конструирование делается через:
    towerFire = fire_constructor( .... );

    Еще одна поправка. Не Rocket, а Missile. Потому что ... :)

    UPD:
    Еще один вариант, уже менее страшный - это шаблонизация базового класса:
    template< class fire_t >
    class TowerParent
    {
    public:
    	TowerParent(int _col, int _row, float _posX, float _posY, float _attackRange, float _angle);
    
    	//...
    
    	fire_t* towerFire;
    
    protected:
    	inline fire_t* MkeFire( float _x, float _y, int _speed, float _angle, float _targetX, float _targetY ){
    		return new fire_t( _x, _y, _speed, _angle, _targetX, _targetY );
    	};
    };
    
    class Bullet : public FireParent<Bullet>
    {
    public:
    	Bullet(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    
    class Rocket : public FireParent<Rocket>
    {
    public:
    	Rocket(float _x, float _y, int _speed, float _angle, float _targetX, float _targetY);
    private:
    };
    Ответ написан
  • Можно ли перегружать базовые типы в С++?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Немного расширю ответ MiiNiPaa.

    Добавить поддержку шаблонных компараторов можно таким образом:
    template< typename item_t, template< typename > class item_traits >
    class item_processor{
    private:
    	typedef item_traits<item_t> traits_t;
    	...
    };


    Изюмина во втором параметре шаблона - это шаблонный параметр шаблона. :)
    У Девида Вандервуда по этому поводу было даже так написано: "Поскольку параметр шаблонного параметра шаблона не используется, его имя можно опустить".
    Такая запись говорит о том, что при инстанцировании или специализации шаблона в его объявлении ожидается только имя шаблона, а не его инстанцирование.

    Используется это дело вот так:
    template< typename target_t >
    class traits_less;
    
    ...
    
    item_processor<char, traits_less> char_processor;


    Все компараторы STL имеют один параметр шаблона. Таким образом, их использование тоже становится допустимо в качестве параметра для item_processor. В то же время, и свой компаратор в такую систему добавить проблемы не составит.
    Ответ написан