【UE4 设计模式】工厂方法模式 Factory Method Pattern 及自定义创建资源

概述

描述

  • 又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式,或者多态工厂(Polymorphic Factory)模式

  • 工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

    image

套路

  • 创建抽象产品类 ;
  • 创建具体产品类,继承抽象产品类;
  • 创建抽象工厂类
  • 创建工厂类,继承抽象工厂类,定义具体产品类的创建方法;
  • 通过调用具体工厂类的方法,从而创建不同具体产品类的实例

使用场景

  • 当一个类不知道它所需要的对象的类时。使用者无需知道具体产品类的类名,只需要知道所对应的工厂即可;
  • 当一个类希望通过其子类来指定创建对象时。由子类来创建具体产品

优缺点

  • 优点
    • 将创建实例的工作与使用实例的工作分开,实现了解耦;
    • 用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
    • 加入新产品时,无须修改抽象工厂和抽象产品提供的接口,只要添加一个具体工厂和具体产品就可以了。这样更符合“开闭原则”。
  • 缺点
    • 在添加新产品时,需要编写新的具体产品类和工厂类,在一定程度上增加了系统的复杂度,
    • 引入抽象层,增加了系统的抽象性和理解难度
    • 一个具体工厂只能创建一种具体产品

UE4 中的工厂方法实践

  • 自定义工厂模式实践
    • 创建产品抽象类和产品具体类

      // 产品抽象类
      UCLASS(Abstract)
      class DESIGNPATTERNS_API UProductObject : public UObject
      {
      	GENERATED_BODY()
      public:
      	virtual void ShowInfo() { check(0 && "You must override this");  }
      };
      
      // 产品具体类A
      UCLASS()
      class DESIGNPATTERNS_API UProductA : public UProductObject
      {
      	GENERATED_BODY()
      public:
      	virtual void ShowInfo() override {
      		UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" This is a ProductA"));
      	}
      };
      
      // 产品具体类B
      UCLASS()
      class DESIGNPATTERNS_API UProductB : public UProductObject
      {
      	GENERATED_BODY()
      public:
      	virtual void ShowInfo() override {
      		UE_LOG(LogTemp, Warning, TEXT(__FUNCTION__" This is a ProductB"));
      	}
      };
      
    • 创建工厂抽象类和工厂具体类

      // 工厂抽象类
      UCLASS(Abstract)
      class DESIGNPATTERNS_API UFactoryObject : public UObject
      {
      	GENERATED_BODY()
      public:
      
      	virtual UProductObject* CreateNewProduct() {
      		check(0 && "You must override this");
      		return nullptr;
      	}
      	
      };
      
      // 工厂具体类A
      UCLASS(Blueprintable,BlueprintType)
      class DESIGNPATTERNS_API UFactoryA : public UFactoryObject
      {
      	GENERATED_BODY()
      public:
      
      	virtual UProductObject* CreateNewProduct() override {
      		return NewObject<UProductA>();
      	}
      };
      
      // 工厂具体类B
      UCLASS(Blueprintable, BlueprintType)
      class DESIGNPATTERNS_API UFactoryB : public UFactoryObject
      {
      	GENERATED_BODY()
      public:
      
      	virtual UProductObject* CreateNewProduct() override {
      		return NewObject<UProductB>();
      	}
      };
      
    • 调用

      // 调用测试用的Actor
      UCLASS()
      class DESIGNPATTERNS_API AFactoryActor : public AActor
      {
      	GENERATED_BODY()
      public:
      	UPROPERTY()
      		UFactoryA* FactoryA;
      
      	UPROPERTY()
      		UFactoryB* FactoryB;
      
      	void BeginPlay() override {
      		// A 工厂生成 A 产品
      		FactoryA = NewObject<UFactoryA>();
      		UProductObject* ProductA = FactoryA->CreateNewProduct();
      		ProductA->ShowInfo();
      
      		// B 工厂生成 B 产品
      		FactoryB = NewObject<UFactoryB>();
      		UProductObject* ProductB = FactoryB->CreateNewProduct();
      		ProductB->ShowInfo();
      	}
      };
      
    • 调式输出

      LogTemp: Warning: UProductA::ShowInfo This is a ProductA
      LogTemp: Warning: UProductB::ShowInfo This is a ProductB
      

引擎自带工厂方法模式——自定义资源创建

  • 继承 UObject 自定义资产类

    UCLASS(Blueprintable, BlueprintType)
    class DESIGNPATTERNS_API UCustomAsset : public UObject
    {
    	GENERATED_BODY()
    public:
    	UPROPERTY(EditAnywhere)
    		FString ProductName;
    };
    
  • 继承 UFactory 的工厂类

    • SupportedClass 指定资产类
    • FactoryCreatNew 重载资产类实例化

    image

    UCLASS()
    class DESIGNPATTERNS_API UUEDefaultFactory : public UFactory
    {
    	GENERATED_UCLASS_BODY()
    public:
    	
    	virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn) override;
    	//virtual uint32 GetMenuCategories() const override; //4.25以前
    };
    
    UUEDefaultFactory::UUEDefaultFactory(const class FObjectInitializer& ObjectInitializer)
    	: Super(ObjectInitializer)
    {
    	bCreateNew = true;
    	bEditAfterNew = true;
    	SupportedClass = UCustomAsset::StaticClass();
    }
    
    UObject* UUEDefaultFactory::FactoryCreateNew(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, FFeedbackContext* Warn)
    {
    	auto newProduct = NewObject<UCustomAsset>(InParent, InClass, InName, Flags);
    	return newProduct;
    }
    
    //uint32 UUEDefaultFactory::GetMenuCategories() const
    //{
    	//return EAssetTypeCategories::Misc;
    //}
    
  • 自定义模块,并添加到工程中

    • 项目目录

      DesignPatterns
      	└─ Source
      	       ├─ DesignPatterns
      	       │    ├─ DesignPatterns.Build.cs
      	       │    ├─ DesignPatterns.cpp
      	       │    ├─ DesignPatterns.h
      	       │    ├─ DesignPatternsGameModeBase.cpp
      	       │    ├─ DesignPatternsGameModeBase.h
      	       │    └─ Patterns_Factory
      	       │         ├─ FactoryObject.cpp
      	       │         ├─ FactoryObject.h
      	       │         ├─ ProductObject.cpp
      	       │         ├─ ProductObject.h
      	       │         ├─ UEDefaultFactory.cpp
      	       │         └─ UEDefaultFactory.h   
      	       ├─ DesignPatternsEditor
      	       │    ├─ DesignPatternsEditor.Build.cs
      	       │    ├─ DesignPatternsEditor.cpp
      	       │    └─ DesignPatternsEditor.h
      	       ├─ DesignPatterns.Target.cs
      	       └─ DesignPatternsEditor.Target.cs
      
    • 添加 DesignPatternsEditor 模块

      • 在Source目录下,分别添加 DesignPatternsEditor.Build.csDesignPatternsEditor.cppDesignPatternsEditor.h 三个文件

        • DesignPatternsEditor.Build.cs

          using UnrealBuildTool;
          
          public class DesignPatternsEditor : ModuleRules
          {
          	public DesignPatternsEditor(ReadOnlyTargetRules Target) : base(Target)
          	{
          		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
          	
          		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "UnrealEd", "DesignPatterns"});
          
          		PrivateDependencyModuleNames.AddRange(new string[] {  });
          
          		// Uncomment if you are using Slate UI
          		PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
          		
          		// Uncomment if you are using online features
          		// PrivateDependencyModuleNames.Add("OnlineSubsystem");
          
          		// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
          	}
          }
          
        • DesignPatternsEditor.h

          • 4.25开始需要自定义 AssetTypeActions(继承自 FAssetTypeActions_Base),才可以在编辑器中找到
          #pragma once
          
          #include "CoreMinimal.h"
          #include "AssetTypeActions_Base.h"
          #include "DesignPatterns/Patterns_Factory/FactoryObject.h"
          
          class FDesignPatternsEditorModule : public IModuleInterface
          {
          public:
          
          	/** IModuleInterface implementation */
          	virtual void StartupModule() override;
          	virtual void ShutdownModule() override;
          };
          
          class FAssetTypeActions_CustomAsset : public FAssetTypeActions_Base
          {
          
          public:
          	FAssetTypeActions_CustomAsset(){}
          	FAssetTypeActions_CustomAsset(EAssetTypeCategories::Type InAssetCategory);
          
          	virtual FColor GetTypeColor() const override { return FColor(97, 85, 212); }
          	virtual void OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor = TSharedPtr<IToolkitHost>()) override;
          
          	// IAssetTypeActions Implementation
          	virtual FText GetName() const override { return FText::FromName(TEXT("Custom Asset")); }
          	virtual UClass* GetSupportedClass() const override { return UCustomAsset::StaticClass(); }
          	virtual uint32 GetCategories() override { return MyAssetCategory; }
          
          private:
          	EAssetTypeCategories::Type MyAssetCategory;
          
          };
          
        • DesignPatternsEditor.cpp

          // Copyright Epic Games, Inc. All Rights Reserved.
          
          #include "DesignPatternsEditor.h"
          #include "Modules/ModuleManager.h"
          #include "IAssetTools.h"
          #include "AssetToolsModule.h"
          
          #define LOCTEXT_NAMESPACE "FDesignPatternsEditorModule"
          
          void FDesignPatternsEditorModule::StartupModule()
          {
          	IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
          	//定义资产的分类名
          	EAssetTypeCategories::Type AssetCategory = AssetTools.RegisterAdvancedAssetCategory(FName(TEXT("CustomKey")), FText::FromName(TEXT("CustomCategory")));
          	TSharedPtr<FAssetTypeActions_CustomAsset> actionType = MakeShareable(new FAssetTypeActions_CustomAsset(AssetCategory));
          	AssetTools.RegisterAssetTypeActions(actionType.ToSharedRef());
          }
          
          void FDesignPatternsEditorModule::ShutdownModule()
          {
          	if (FModuleManager::Get().IsModuleLoaded("AssetTools"))
          	{
          		IAssetTools& AssetTools = FModuleManager::Get().GetModuleChecked<FAssetToolsModule>("AssetTools").Get();
          		AssetTools.UnregisterAssetTypeActions(MakeShareable(new FAssetTypeActions_CustomAsset));
          	}
          }
          
          FAssetTypeActions_CustomAsset::FAssetTypeActions_CustomAsset(EAssetTypeCategories::Type InAssetCategory)
          {
          	MyAssetCategory = InAssetCategory;
          }
          
          void FAssetTypeActions_CustomAsset::OpenAssetEditor(const TArray<UObject*>& InObjects, TSharedPtr<class IToolkitHost> EditWithinLevelEditor /*= TSharedPtr<IToolkitHost>()*/)
          {
          	FSimpleAssetEditor::CreateEditor(EToolkitMode::Standalone, EditWithinLevelEditor, InObjects);
          }
          
          #undef LOCTEXT_NAMESPACE
          IMPLEMENT_MODULE(FDesignPatternsEditorModule, DesignPatternsEditor);
          
      • 将模块加入到 项目名.uproject

        {
        	"EngineAssociation": "4.26",
        	"Modules": [
        		...,
        		{
        			"Name": "DesignPatternsEditor",
        			"Type": "Editor",
        			"LoadingPhase": "Default",
        			"AdditionalDependencies": [
        				"Engine",
        				"CoreUObject",
        				"Slate",
        				"SlateCore",
        				"UnrealEd"
        			]
        		}
        	]
        }
        
      • 将模块加入到 项目名.Target.cs

        using UnrealBuildTool;
        using System.Collections.Generic;
        
        public class DesignPatternsEditorTarget : TargetRules
        {
        	public DesignPatternsEditorTarget( TargetInfo Target) : base(Target)
        	{
        		Type = TargetType.Editor;
        		DefaultBuildSettings = BuildSettingsVersion.V2;
        		ExtraModuleNames.AddRange( new string[] { "DesignPatterns", "DesignPatternsEditor" } );
        	}
        }
        
  • 测试结果

    image


参考

posted @ 2021-06-03 23:33  砥才人  阅读(1013)  评论(0编辑  收藏  举报