【问题标题】:when refactoring using extract method, should you extract into a new class or into the current class?使用 extract 方法重构时,应该提取到新类还是当前类?
【发布时间】:2020-12-30 12:34:23
【问题描述】:

我一直在使用提取方法来使我的代码流更易于理解,但现在我将大量这些方法转储到我的类文件的底部。我曾考虑为它们的方法或组创建单独的类,但我只是担心这是否会增加不必要的复杂性。任何优雅地做到这一点的提示将不胜感激。

示例 - 我知道这很庞大并且违反了几个原则,我现在正在积极研究 SOLID 原则,但我已经养成了多年的坏习惯。

这是一个自定义 GUI 检查器,用于在我的游戏中制作过场动画。

 #if UNITY_EDITOR
[CustomEditor(typeof(Cutscene))]
public class CutsceneEditor: Editor
{
    GameObject parent;
    Cutscene cutscene;
    GameObject currentMarker = null;
    SerializedProperty startMarker, sequenceList;
    string[] actionOptions = {"Movement", "Poses"};
    float scrollbarOffset;
    GUIContent noLabel = new GUIContent();
    List<bool> foldouts;
    int currentFoldout;
    string hideText;

    void OnEnable() 
    {
        cutscene = (Cutscene)target;
        parent = GameObject.FindGameObjectWithTag("PathUI");
        startMarker = serializedObject.FindProperty("startMarker");
        sequenceList = serializedObject.FindProperty("cutsceneSequences");   
        noLabel.text = "";
        foldouts = new List<bool>();
        //IsPathIsVisible();

        if (Application.isPlaying == false && (IsPathIsVisible() == true))
        {
            DrawCutscenePath(1);
        }

        foreach(SerializedProperty Cs in sequenceList)
        {
            foldouts.Add(false);
        }
    }

    public override void OnInspectorGUI() 
    {
        if (Application.isPlaying == false)
        {
            DisplayCutsceneTabs();
        }
    }

    void DisplayCutsceneTabs()
    {
        for (int i = 0; i < sequenceList.arraySize; i ++)
            {
                EditorGUILayout.Space(5);
                var sequenceElement = sequenceList.GetArrayElementAtIndex(i);
                var actionMarker = serializedObject.FindProperty("actionMarker");
                var actionMarkerArrow = serializedObject.FindProperty("actionMarkerArrow");
                var startPoint = sequenceElement.FindPropertyRelative("cutsceneStartPoint");
                var startRotation = sequenceElement.FindPropertyRelative("cutsceneStartRotation");
                var cutsceneAction = sequenceElement.FindPropertyRelative("cutsceneAction");
                var actionSelection = sequenceElement.FindPropertyRelative("actionSelectionIndex");
                var timeToWait = sequenceElement.FindPropertyRelative("timeToWait");
                var maxTimeToWait = sequenceElement.FindPropertyRelative("maxTimeToWait");
                var voiceClip = sequenceElement.FindPropertyRelative("voiceClip");
                var hasDialogue = sequenceElement.FindPropertyRelative("hasDialogue");
                var voiceClipDelay = sequenceElement.FindPropertyRelative("voiceClipDelay");
                var voiceClipVolume = sequenceElement.FindPropertyRelative("voiceClipVolume");
            
                if (i == 0)
                {
                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.LabelField("Start Point Object");
                    EditorGUILayout.LabelField("Cutscene Markers");
                    EditorGUILayout.EndHorizontal();

                    EditorGUILayout.BeginHorizontal();
                    EditorGUILayout.PropertyField(startMarker, noLabel);
                    EditorGUILayout.PropertyField(actionMarker, noLabel);
                    EditorGUILayout.PropertyField(actionMarkerArrow, noLabel);
                    EditorGUILayout.EndHorizontal();
                    EditorGUILayout.Space(10);

                    // set start position to start markers position
                    if (startMarker.objectReferenceValue != null)
                    {
                        GameObject startObject = startMarker.objectReferenceValue as GameObject;
                        startPoint.vector3Value = startObject.transform.position;
                        startRotation.quaternionValue = startObject.transform.rotation;
                    }
                    HandleButtons(0);
                    DrawBlackBox();
                    continue;
                }
                
                
                if (foldouts[i] = EditorGUILayout.BeginFoldoutHeaderGroup(foldouts[i], "Cutscene Action " + "#" + " " + i))
                {
                    currentFoldout = i;
                    CloseAlmostAllFoldouts(i);

                    if (i > 0)
                    {
                        EditorGUILayout.BeginHorizontal();

                        actionSelection.intValue = EditorGUILayout.Popup(actionSelection.intValue, actionOptions, GUILayout.MaxWidth(100));
                        if (actionSelection.intValue == 0)
                        {
                            cutsceneAction.objectReferenceValue = EditorGUILayout.ObjectField(cutsceneAction.objectReferenceValue, typeof(MovementAction), false) as MovementAction;
                        }
                        if (actionSelection.intValue == 1)
                        {
                            cutsceneAction.objectReferenceValue = EditorGUILayout.ObjectField(cutsceneAction.objectReferenceValue, typeof(PoseAction), false) as PoseAction;
                        }
                        EditorGUI.BeginChangeCheck();
                        EditorGUILayout.EndHorizontal();

                        EditorGUILayout.BeginHorizontal();
                        
                        timeToWait.floatValue = EditorGUILayout.Slider(timeToWait.floatValue, 0, maxTimeToWait.floatValue);
                        maxTimeToWait.floatValue = EditorGUILayout.FloatField(maxTimeToWait.floatValue,  GUILayout.MaxWidth(25));

                        if(EditorGUI.EndChangeCheck())
                        {
                            DrawCutscenePath(i);
                        }            
                        EditorGUILayout.EndHorizontal();
                    }

                    scrollbarOffset = (sequenceList.arraySize < 8) ? 20 : 32;

                    hasDialogue.boolValue = EditorGUILayout.ToggleLeft("Show Voice Options", hasDialogue.boolValue);

                    if (hasDialogue.boolValue == true)
                    {
                        EditorGUILayout.PropertyField(voiceClip);
                        voiceClipVolume.floatValue = EditorGUILayout.Slider("Volume", voiceClipVolume.floatValue, 0, 2);
                        voiceClipDelay.floatValue = EditorGUILayout.Slider("Delay", voiceClipDelay.floatValue, 0, maxTimeToWait.floatValue);
                        
                        if (voiceClip.objectReferenceValue != null)
                        {
                            if (GUILayout.Button("Remove Voice Clip"))
                            {
                                voiceClip.objectReferenceValue = null;
                                voiceClipVolume.floatValue = 1;
                                voiceClipDelay.floatValue = 0;
                            }
                        }
                    }
                    
                    HandleButtons(i);
                    DrawBlackBox();        
                    serializedObject.ApplyModifiedProperties();
                }
                EditorGUILayout.EndFoldoutHeaderGroup();
                if (i == sequenceList.arraySize - 1)
                {
                    var DrawPathButtonArea = EditorGUILayout.BeginVertical();
                    EditorGUILayout.Space(40);
                    EditorGUILayout.EndVertical();

                    if (GUI.Button(new Rect(0, DrawPathButtonArea.y, Screen.width, 20), (hideText != null)? hideText : "Hide Cutscene Tab"))
                    {
                        HideCutscenePath();
                    }
                }
            }
    }

    void HandleButtons(int i)
    {
        var tabEnd = EditorGUILayout.BeginHorizontal();
        EditorGUILayout.EndHorizontal();

        if (GUI.Button(new Rect(0, tabEnd.y + 15, 75, 20), "New Move"))
        {
            sequenceList.InsertArrayElementAtIndex(i);
            CutsceneSequenceDisplay.InitializeNewMove(sequenceList.GetArrayElementAtIndex(i + 1));
            foldouts.Insert(i, false);
            if (i != 0)
            DrawCutscenePath(i);
        }
        if (i > 0)
        {
            if (GUI.Button(new Rect(Screen.width - scrollbarOffset - 20, tabEnd.y + 15, 20, 20), "D"))
            {
                foldouts.Insert(i, false);
                sequenceList.InsertArrayElementAtIndex(i);
                DrawCutscenePath(i);
                CloseAlmostAllFoldouts(i);
            }
            if (GUI.Button(new Rect(Screen.width - scrollbarOffset, tabEnd.y + 15, 20, 20), "X"))
            {
                if (i != 0)
                {          
                    foldouts.RemoveAt(i);
                    sequenceList.DeleteArrayElementAtIndex(i);   
                    DrawCutscenePath((i)=i != 1 ? i - 1 : i);          
                }
                else if (sequenceList.arraySize > 0)
                {
                    foldouts.RemoveAt(i + 1);
                    sequenceList.DeleteArrayElementAtIndex(i + 1);
                }         
            }
        }
        serializedObject.ApplyModifiedProperties();
    }

    void CloseAlmostAllFoldouts(int index)
    {
        for (int i = 0; i < foldouts.Count; i++)
        {
            if(i != index)
            {
                foldouts[i] = false;  
            }
        }
    }

    void DrawCutscenePath(int index)
    {
        GameObject[] parents = GameObject.FindGameObjectsWithTag("ParentUI");
       if (IsPathIsVisible() == true || parents.Length > -1) 
       {

            if (parents.Length != 0)
            {
                for(int i = index - 1; i < parents.Length; i++)
                {
                    if (parents[i].activeSelf == true)
                    {
                        DestroyImmediate(parents[i].gameObject);
                    }
                }
            }      
            for (int i = index; i < sequenceList.arraySize; i ++)
            {
                var thisSequenceElement = sequenceList.GetArrayElementAtIndex(i);
                var cutsceneAction = thisSequenceElement.FindPropertyRelative("cutsceneAction").objectReferenceValue;

                if (cutsceneAction is MovementAction)
                {
                    CutsceneSequenceDisplay.ProcessMovementPathing(i, sequenceList, serializedObject, ref currentMarker);
                }
                if (cutsceneAction is PoseAction)
                {
                    CutsceneSequenceDisplay.ProcessPosePathing(i, sequenceList, serializedObject, ref currentMarker);
                }
            }    
       }
    }

    bool IsPathIsVisible()
    {
        foreach (Transform child in parent.transform)
        {
            if (child.gameObject.activeSelf == true)
            {
                return true;
            }
        }
        return false;
    }

    void HideCutscenePath()
    {    
        if (parent != null)
        {
            bool isPathVisible = false;

            foreach (Transform child in parent.transform)
            {
                child.gameObject.SetActive(!child.gameObject.activeSelf);
                isPathVisible = child.gameObject.activeSelf;
            }
            hideText = (isPathVisible) ? "Hide Cutscene Path" : "Show Cutscene Path";
        }
            
    }

    void DrawBlackBox()
    {
        Color boxColor = new Color(0,0,0,0.2f);
        EditorGUILayout.Space(15); 
        var position = EditorGUILayout.BeginVertical();
        EditorGUI.DrawRect(new Rect(0,position.y,Screen.width,20), boxColor);
        EditorGUILayout.EndVertical();
        EditorGUILayout.Space(20); 
    }

}

#endif

【问题讨论】:

  • 最好提供一些示例代码。在不同的情况下,提取为同一个类方法和提取整个抽象到单独的类都是有意义的。
  • 好的,我在上面放了一些代码@blami 我知道我可能违反了很多原则,我有很多坏习惯。

标签: oop design-patterns refactoring extract


【解决方案1】:

嗯,这...取决于。作为一般的经验法则,尽量减少类中私有方法的数量,它们总是一个不好的迹象。遵循单一职责原则:一个类应该只做一件事。

此外,优先考虑组合而不是继承:处理长继承链可能会变得复杂并导致严重的错误。改为使用组件来定义对象的行为。

无论如何,如果您发布一些示例代码,我们可能会更具体:)

【讨论】:

  • 好吧,这是我第一次诚实地分享我的代码,这有点尴尬,我是自学成才,最近才开始阅读有关 SOLID 原则/设计模式和重构的内容,我知道我有很多需要改进的地方,所以任何提示也将不胜感激。
猜你喜欢
  • 2023-03-14
  • 2011-01-31
  • 2011-10-20
  • 2016-10-21
  • 1970-01-01
  • 1970-01-01
  • 2012-07-24
  • 1970-01-01
相关资源
最近更新 更多