This is for beginners who want to try using design patterns in Maya Python. I will leave the detailed explanation about the design pattern to other sites, How does the code improve when you actually use it? Focus on. As an application example, let's take something like a data exporter as a sample.
I want to improve the design power and code quality by actually using the design pattern. I want to find a pattern that can be used especially in tool implementation.
The following pattern is used this time. For details of the pattern, please check the reference site at the bottom of the page.
pattern | Overview (my perception) | merit |
---|---|---|
TemplateMethod | Abstract the method called in the parent class and leave the implementation inside the method to the subclass. | Process flowOf each process while bindingBehaviorIs left to the subclass. |
FactoryMethod | If you want to use another object in the class, abstract the generation process and use the (abstracted) generation object. The generation process and method implementation are left to each subclass. | It inherits the goodness of Template Method. When creating another object and calling a methodProcess flow・Generate・BehaviorCan be separated. |
Factory | Prepare and use a class that only creates objects according to the specified conditions. | From the side that uses the object, the object according to the conditionsGenerateCan be separated. |
I thought that exporting Maya scene information was a relatively large process, so I chose it. The image is like outputting appropriate data using FBX export. There is a recognition that the specifications are likely to be changed later, such as selecting output data or performing special processing depending on the conditions. It describes what the code will look like before and after applying the design pattern.
If you arrange the processes without thinking about anything in particular, it will look like the following.
scene_exporter.py
def scene_export(option):
"""Output models and animations in the scene"""
#Pre-processing
if option.mode == ExportMode.head:
#Find Head mesh data in Hierarchy
#Do something
#Export
if option.mode == ExportMode.body:
#Find Body mesh data in Hierarchy
#Do something
#Export
if option.mode == ExportMode.animation:
#Find Skeleton data in the Hierarchy
#Do something
#Export
#Post-processing
For the time being, apply the Template Method pattern to standardize the export process.
exporter.py
from abc import ABCMeta, abstractmethod
class BaseExporter(object):
"""Exporter basic class"""
__metaclass__ = ABCMeta
def export(self):
select_export_targets()
export_selection()
@abstractmethod
def select_export_targets(self):
"""Select the export target"""
pass
@abstractmethod
def export_selection(self):
"""Export selected objects"""
pass
class HeadExporter(BaseExporter):
"""Exporter class for HEAD"""
def select_export_targets(self):
cmds.select("|char|mesh|head")
def export_selection(self):
#Implemented output processing for HEAD
class BodyExporter(BaseExporter):
"""Exporter class for BODY"""
def select_export_targets(self):
cmds.select("|char|mesh|body")
def export_selection(self):
#Implemented output processing for BODY
class AnimExporter(BaseExporter):
"""Exporter class for ANIM"""
def select_export_targets(self):
cmds.select("|char|skel|root")
def export_selection(self):
#Implemented output processing for ANIM
scene_exporter_v2.py
def scene_export(option):
"""Output models and animations in the scene"""
#Pre-processing
if option.mode == ExportMode.head:
HeadExporter().export()
if option.mode == ExportMode.body:
BodyExporter().export()
if option.mode == ExportMode.animation:
AnimExporter().export()
#Post-processing
Even if TemplateMethod is applied, the maintainability when adding a mode has not been improved yet. Create a new Factory class and let it branch and generate. The main process just uses the BaseExporter subclass where export () exists.
exporter_factory.py
class BaseExporterFactory(object):
"""Factory class of BaseExporter class"""
def create(option):
if option.mode == ExportMode.head:
return HeadExporter()
if option.mode == ExportMode.body:
return BodyExporter()
if option.mode == ExportMode.animation:
return AnimExporter()
scene_exporter_v3.py
def scene_export(option):
"""Output models and animations in the scene"""
#Pre-processing
BaseExporterFactory().create(option).export()
#Post-processing
When the specification is added, we will "create and use a log object inside the BaseExporter class". Apply the FactoryMethod because you will be creating and using objects in the BaseExporter class.
Implement the parent class and subclass of ExporterLog, which is the Product of FactoryMethod.
exporter_log.py
from abc import ABCMeta, abstractmethod
class BaseExporterLog(object):
"""Exporter log base class"""
__metaclass__ = ABCMeta
@abstractmethod
def open(self):
"""Start logging"""
pass
@abstractmethod
def close(self):
"""End logging"""
pass
class MyExporterLog(BaseExporterLog):
"""Exporter log subclass"""
def open(self):
print "export logging start"
def close(self):
print "export logging close"
Define the generation and use of logs in BaseExporter, which is the Creator of FactoryMethod, and leave the implementation to the subclass.
exporter.py
from abc import ABCMeta, abstractmethod
class BaseExporter(object):
"""Exporter basic class"""
__metaclass__ = ABCMeta
def export(self):
self.log = create_exporter_log()
self.log.open()
select_export_targets()
export_selection()
self.log.close()
@abstractmethod
def create_exporter_log(self):
"""Generate an export log"""
pass
@abstractmethod
def select_export_targets(self):
"""Select the export target"""
pass
@abstractmethod
def export_selection(self):
"""Export selected objects"""
pass
class HeadExporter(BaseExporter):
"""Exporter class for HEAD"""
def create_exporter_log(self):
return MyExporterLog()
def select_export_targets(self):
cmds.select("|char|mesh|head")
def export_selection(self):
#Implemented output processing for HEAD
After that, I think that it may or may not be like using logs on the main processing side.
scene_exporter_v4.py
def scene_export(option):
"""Output models and animations in the scene"""
#Pre-processing
exporter = BaseExporterFactory().create(option)
exporter.export()
#Whether or not to process the log
#Post-processing
Regardless of whether the recognition and examples are appropriate, looking at the code after pattern adaptation is somewhat convincing. If it's just the amount of code, the base code is the smallest, but considering that it will be changed later, I used a pattern It's useful to spend some time refactoring.
With the pattern used this time, I find it difficult to understand the Factory Method pattern. It feels like a Derivation of the Factory pattern from the name, but I recognized that it is a derivation of the Template Method. Because I thought so, the flow of pattern adaptation also brought the Template Method first.
I would like to continue studying while waiting for advice and advice from various fields.
-1. Ruby Design Pattern [Template Method] --Shred IT !!!! -4. Ruby Design Pattern [Factory Method] --Shred IT !!!! -3. TemplateMethod pattern | TECHSCORE -4. FactoryMethod pattern | TECHSCORE
Recommended Posts