>
Rubberduck의 최신 리팩토링 중 하나는 Extract Interface입니다. 이 리팩토링은 수업을 수강하고 모든 공개 회원을 표시하며 인터페이스에 포함 할 회원을 선택할 수 있습니다. 다음으로 인터페이스를 만들고 Implement <Interface Name> 를 추가합니다.  파일 맨 위로 이동하고 인터페이스 구현 을 호출하여 빈 인터페이스 멤버를 구현합니다. 안타깝게도 참조를 구문 분석하고 해결해야하는 시간으로 인해 기존 멤버의 이름을 바꾸지 않기로 결정했습니다.

리팩토링 모델입니다 :

public class ExtractInterfaceModel
{
    private readonly RubberduckParserState _parseResult;
    public RubberduckParserState ParseResult { get { return _parseResult; } }
    private readonly IEnumerable<Declaration> _declarations;
    public IEnumerable<Declaration> Declarations { get { return _declarations; } }
    private readonly QualifiedSelection _selection;
    public QualifiedSelection Selection { get { return _selection; } }
    private readonly Declaration _targetDeclaration;
    public Declaration TargetDeclaration { get { return _targetDeclaration; } }
    public string InterfaceName { get; set; }
    public List<InterfaceMember> Members { get; set; }
    private static readonly DeclarationType[] DeclarationTypes =
    {
        DeclarationType.Class,
        DeclarationType.Document,
        DeclarationType.UserForm
    };
    public readonly string[] PrimitiveTypes =
    {
        Tokens.Boolean,
        Tokens.Byte,
        Tokens.Date,
        Tokens.Decimal,
        Tokens.Double,
        Tokens.Long,
        Tokens.LongLong,
        Tokens.LongPtr,
        Tokens.Integer,
        Tokens.Single,
        Tokens.String,
        Tokens.StrPtr
    };
    public ExtractInterfaceModel(RubberduckParserState parseResult, QualifiedSelection selection)
    {
        _parseResult = parseResult;
        _selection = selection;
        _declarations = parseResult.AllDeclarations.ToList();
        _targetDeclaration =
            _declarations.SingleOrDefault(
                item =>
                    !item.IsBuiltIn && DeclarationTypes.Contains(item.DeclarationType)
                    && item.Project == selection.QualifiedName.Project
                    && item.QualifiedSelection.QualifiedName == selection.QualifiedName);
        InterfaceName = "I" + TargetDeclaration.IdentifierName;
         Members = _declarations.Where(item => !item.IsBuiltIn &&
                                            item.Project == _targetDeclaration.Project &&
                                            item.ComponentName == _targetDeclaration.ComponentName &&
                                            item.Accessibility == Accessibility.Public &&
                                            item.DeclarationType != DeclarationType.Variable &&
                                            item.DeclarationType != DeclarationType.Event)
                                 .OrderBy(o => o.Selection.StartLine)
                                 .ThenBy(t => t.Selection.StartColumn)
                                 .Select(d => new InterfaceMember(d, _declarations))
                                 .ToList();
    }
}

발표자 :

public interface IExtractInterfacePresenter
{
    ExtractInterfaceModel Show();
}
public class ExtractInterfacePresenter : IExtractInterfacePresenter
{
    private readonly IExtractInterfaceView _view;
    private readonly ExtractInterfaceModel _model;
    public ExtractInterfacePresenter(IExtractInterfaceView view, ExtractInterfaceModel model)
    {
        _view = view;
        _model = model;
    }
    public ExtractInterfaceModel Show()
    {
        if (_model.TargetDeclaration == null) { return null; }
        _view.ComponentNames =
            _model.TargetDeclaration.Project.VBComponents.Cast<VBComponent>().Select(c => c.Name).ToList();
        _view.InterfaceName = _model.InterfaceName;
        _view.Members = _model.Members;
        if (_view.ShowDialog() != DialogResult.OK)
        {
            return null;
        }
        _model.InterfaceName = _view.InterfaceName;
        _model.Members = _view.Members;
        return _model;
    }
}

다음으로 리팩토링이 온다 :

public class ExtractInterfaceRefactoring : IRefactoring
{
    private readonly RubberduckParserState _state;
    private readonly IRefactoringPresenterFactory<ExtractInterfacePresenter> _factory;
    private readonly IActiveCodePaneEditor _editor;
    private ExtractInterfaceModel _model;
    public ExtractInterfaceRefactoring(RubberduckParserState state, IRefactoringPresenterFactory<ExtractInterfacePresenter> factory,
        IActiveCodePaneEditor editor)
    {
        _state = state;
        _factory = factory;
        _editor = editor;
    }
    public void Refactor()
    {
        var presenter = _factory.Create();
        if (presenter == null)
        {
            return;
        }
        _model = presenter.Show();
        if (_model == null) { return; }
        AddInterface();
    }
    public void Refactor(QualifiedSelection target)
    {
        _editor.SetSelection(target);
        Refactor();
    }
    public void Refactor(Declaration target)
    {
        _editor.SetSelection(target.QualifiedSelection);
        Refactor();
    }
    private void AddInterface()
    {
        var interfaceComponent = _model.TargetDeclaration.Project.VBComponents.Add(vbext_ComponentType.vbext_ct_ClassModule);
        interfaceComponent.Name = _model.InterfaceName;
        _editor.InsertLines(1, GetInterface());
        var module = _model.TargetDeclaration.QualifiedSelection.QualifiedName.Component.CodeModule;
        var implementsLine = module.CountOfDeclarationLines + 1;
        module.InsertLines(implementsLine, "Implements " + _model.InterfaceName);
        _state.RequestParse(ParserState.Ready);
        var qualifiedSelection = new QualifiedSelection(_model.TargetDeclaration.QualifiedSelection.QualifiedName,
            new Selection(implementsLine, 1, implementsLine, 1));
        var implementInterfaceRefactoring = new ImplementInterfaceRefactoring(_state, _editor, new MessageBox());
        implementInterfaceRefactoring.Refactor(qualifiedSelection);
    }
    private string GetInterface()
    {
        return "Option Explicit" + Environment.NewLine + string.Join(Environment.NewLine, _model.Members.Where(m => m.IsSelected));
    }
}

지원 클래스 InterfaceMember  그리고 Parameter :

public class Parameter
{
    public string ParamAccessibility { get; set; }
    public string ParamName { get; set; }
    public string ParamType { get; set; }
    public override string ToString()
    {
        return ParamAccessibility + " " + ParamName + " As " + ParamType;
    }
}
public class InterfaceMember
{
    public Declaration Member { get; set; }
    public IEnumerable<Parameter> MemberParams { get; set; }
    public string Type { get; set; }
    public string MemberType { get; set; }
    public string PropertyType { get; set; }
    public bool IsSelected { get; set; }
    public string MemberSignature
    {
        get
        {
            var signature = Member.IdentifierName + "(" +
                string.Join(", ", MemberParams.Select(m => m.ParamType)) + ")";
            return Type == null ? signature : signature + " As " + Type;
        }
    }
    public string FullMemberSignature
    {
        get
        {
            var signature = Member.IdentifierName + "(" +
                string.Join(", ", MemberParams) + ")";
            return Type == null ? signature : signature + " As " + Type;
        }
    }
    public InterfaceMember(Declaration member, IEnumerable<Declaration> declarations)
    {
        Member = member;
        Type = member.AsTypeName;
        GetMethodType();
        MemberParams = declarations.Where(item => item.DeclarationType == DeclarationType.Parameter &&
                                      item.ParentScope == Member.Scope)
                                   .OrderBy(o => o.Selection.StartLine)
                                   .ThenBy(t => t.Selection.StartColumn)
                                   .Select(p => new Parameter
                                   {
                                       ParamAccessibility = ((VBAParser.ArgContext)p.Context).BYREF() == null ? Tokens.ByVal : Tokens.ByRef,
                                       ParamName = p.IdentifierName,
                                       ParamType = p.AsTypeName
                                   })
                                   .ToList();
        if (PropertyType == "Get")
        {
            MemberParams = MemberParams.Take(MemberParams.Count() - 1);
        }

        IsSelected = false;
    }
    private void GetMethodType()
    {
        var context = Member.Context;
        var subStmtContext = context as VBAParser.SubStmtContext;
        if (subStmtContext != null)
        {
            MemberType = Tokens.Sub;
        }
        var functionStmtContext = context as VBAParser.FunctionStmtContext;
        if (functionStmtContext != null)
        {
            MemberType = Tokens.Function;
        }
        var propertyGetStmtContext = context as VBAParser.PropertyGetStmtContext;
        if (propertyGetStmtContext != null)
        {
            MemberType = Tokens.Property;
            PropertyType = Tokens.Get;
        }
        var propertyLetStmtContext = context as VBAParser.PropertyLetStmtContext;
        if (propertyLetStmtContext != null)
        {
            MemberType = Tokens.Property;
            PropertyType = Tokens.Let;
        }
        var propertySetStmtContext = context as VBAParser.PropertySetStmtContext;
        if (propertySetStmtContext != null)
        {
            MemberType = Tokens.Property;
            PropertyType = Tokens.Set;
        }
    }
    public override string ToString()
    {
        return "Public " + MemberType + " " + PropertyType + " " + FullMemberSignature + Environment.NewLine + "End " + MemberType +
               Environment.NewLine;
    }
}

이 대화 상자 코드는 다음과 같습니다 :

public partial class ExtractInterfaceDialog : Form, IExtractInterfaceView
{
    public string InterfaceName
    {
        get { return InterfaceNameBox.Text; }
        set { InterfaceNameBox.Text = value; }
    }
    private List<InterfaceMember> _members;
    public List<InterfaceMember> Members
    {
        get { return _members; }
        set
        {
            _members = value;
            InitializeParameterGrid();
        }
    }
    public List<string> ComponentNames { get; set; }
    public ExtractInterfaceDialog()
    {
        InitializeComponent();
        InterfaceNameBox.TextChanged += InterfaceNameBox_TextChanged;
        InterfaceMembersGridView.CellValueChanged += InterfaceMembersGridView_CellValueChanged;
        SelectAllButton.Click += SelectAllButton_Click;
        DeselectAllButton.Click += DeselectAllButton_Click;
    }
    void InterfaceNameBox_TextChanged(object sender, EventArgs e)
    {
        ValidateNewName();
    }
    void InterfaceMembersGridView_CellValueChanged(object sender, DataGridViewCellEventArgs e)
    {
        _members.ElementAt(e.RowIndex).IsSelected =
            (bool) InterfaceMembersGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
    }
    void SelectAllButton_Click(object sender, EventArgs e)
    {
        ToggleSelection(true);
    }
    void DeselectAllButton_Click(object sender, EventArgs e)
    {
        ToggleSelection(false);
    }
    private void InitializeParameterGrid()
    {
        InterfaceMembersGridView.AutoGenerateColumns = false;
        InterfaceMembersGridView.Columns.Clear();
        InterfaceMembersGridView.DataSource = Members;
        InterfaceMembersGridView.AlternatingRowsDefaultCellStyle.BackColor = Color.Lavender;
        InterfaceMembersGridView.MultiSelect = false;
        var isSelected = new DataGridViewCheckBoxColumn
        {
            AutoSizeMode = DataGridViewAutoSizeColumnMode.AllCells,
            Name = "IsSelected",
            DataPropertyName = "IsSelected",
            HeaderText = string.Empty,
            ReadOnly = false
        };
        var signature = new DataGridViewTextBoxColumn
        {
            AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill,
            Name = "Members",
            DataPropertyName = "MemberSignature",
            ReadOnly = true
        };
        InterfaceMembersGridView.Columns.AddRange(isSelected, signature);
    }
    void ToggleSelection(bool state)
    {
        foreach (var row in InterfaceMembersGridView.Rows.Cast<DataGridViewRow>())
        {
            row.Cells["IsSelected"].Value = state;
        }
    }
    private void ValidateNewName()
    {
        var tokenValues = typeof(Tokens).GetFields().Select(item => item.GetValue(null)).Cast<string>().Select(item => item);
        OkButton.Enabled = !ComponentNames.Contains(InterfaceName)
                           && char.IsLetter(InterfaceName.FirstOrDefault())
                           && !tokenValues.Contains(InterfaceName, StringComparer.InvariantCultureIgnoreCase)
                           && !InterfaceName.Any(c => !char.IsLetterOrDigit(c) && c != '_');
        InvalidNameValidationIcon.Visible = !OkButton.Enabled;
    }
}

이 코드로 개선 할 수있는 모든 것을 알려주십시오. 사소한 개선이 많을수록 좋습니다. Nitpicks도 환영합니다.

  • 답변 # 1

    이 코드 섹션은 나에게 재미있다.

    와이즈 비즈 와이즈 비즈

    이것은 다른 학급에 속한다고 생각합니다. 나는 그 클래스의 이름을 무엇으로할지 확실하지 않지만 이것들은 파서에 더 가깝습니다. 어쩌면 그들은

    의 정적 회원입니다
     그리고 
    private static readonly DeclarationType[] DeclarationTypes =
    {
        DeclarationType.Class,
        DeclarationType.Document,
        DeclarationType.UserForm
    };
    public readonly string[] PrimitiveTypes =
    {
        Tokens.Boolean,
        Tokens.Byte,
        Tokens.Date,
        Tokens.Decimal,
        Tokens.Double,
        Tokens.Long,
        Tokens.LongLong,
        Tokens.LongPtr,
        Tokens.Integer,
        Tokens.Single,
        Tokens.String,
        Tokens.StrPtr
    }
    
     각기. 어쩌면 그들은 확장 방법이거나 어쩌면 둘 다 도우미 클래스에 속할 수도 있지만 다른 
    DeclarationTypes
    가 없다고 생각하는 데 어려움을 겪고 있습니다.
    어떤 토큰이 기본 유형인지 또는 어떤 선언 유형이 클래스인지 알아야합니다.

    이것의 다른 문제는 누군가 어리석은 일이라면 배열을 수정할 수 있다는 것입니다.

    와이즈 비즈 와이즈 비즈

    와이즈 비즈  수정자는 식별자에 다른 참조를 할당 할 수 없음을 의미합니다. 우리가 배열의 내부를 수정하는 것을 막는 것은 없습니다. 일종의 ReadOnlyCollection에 도달했습니다. 와이즈 비즈  는 기본 클래스로 설계되었으므로 유용한 스 니펫을 재사용 가능한 코드로 추출 할 수있는 또 하나의 이유가됩니다.

    Tokens

  • 이전 java - Box2d와 농구 충돌
  • 다음 플러터 아이콘이 표시되지 않습니다