清洁代码:可读的依赖注入建议?

时间:2010-09-29 18:33:48

标签: c# dependency-injection autocad coding-style

我有一个向AutoCad绘图添加元素的项目。我注意到我开始在多种方法中编写相同的十行代码(为简单起见仅显示两行)。

初步实施: 你会注意到唯一真正改变的是添加一个Line而不是一个Circle。

[CommandMethod("Test", CommandFlags.Session)]
    public void Test()
    {
        AddLineToDrawing();
        AddCircleToDrawing();
    }

    private void AddLineToDrawing()
    {
        using (DocumentLock lockedDocument = Application.DocumentManager.MdiActiveDocument.LockDocument())
        {
            using (Database database = Application.DocumentManager.MdiActiveDocument.Database)
            {
                using (Transaction transaction = database.TransactionManager.StartTransaction())//Start the transaction
                {
                    //Open the block table for read
                    BlockTable blockTable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;

                    //Open the block table record model space for write
                    BlockTableRecord blockTableRecord = (BlockTableRecord)transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    Line line = new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0));
                    blockTableRecord.AppendEntity(line);

                    transaction.AddNewlyCreatedDBObject(line, true);

                    transaction.Commit();
                }
            }
        }
    }

    private void AddCircleToDrawing()
    {
        using (DocumentLock lockedDocument = Application.DocumentManager.MdiActiveDocument.LockDocument())
        {
            using (Database database = Application.DocumentManager.MdiActiveDocument.Database)
            {
                using (Transaction transaction = database.TransactionManager.StartTransaction())//Start the transaction
                {
                    //Open the block table for read
                    BlockTable blockTable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;

                    //Open the block table record model space for write
                    BlockTableRecord blockTableRecord = (BlockTableRecord)transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    Circle circle = new Circle(new Point3d(0, 0, 0), new Vector3d(0, 0, 0), 10);
                    blockTableRecord.AppendEntity(circle);

                    transaction.AddNewlyCreatedDBObject(circle, true);

                    transaction.Commit();
                }
            }
        }
    }

注入:这接近删除了代码重复,但我认为可读性差。

[CommandMethod("Test", CommandFlags.Session)]
    public void Test()
    {
        PerformActionOnBlockTable(new CircleDrawer());
        PerformActionOnBlockTable(new LineDrawer());
    }

    public interface IDraw
    {
        DBObject DrawObject(BlockTableRecord blockTableRecord);
    }

    public class CircleDrawer : IDraw
    {
        public DBObject DrawObject(BlockTableRecord blockTableRecord)
        {
            Circle circle = new Circle(new Point3d(0, 0, 0), new Vector3d(0, 0, 0), 10);
            blockTableRecord.AppendEntity(circle);

            return circle;
        }
    }

    public class LineDrawer : IDraw
    {
        public DBObject DrawObject(BlockTableRecord blockTableRecord)
        {
            Line line = new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0));
            blockTableRecord.AppendEntity(line);

            return line;
        }
    }

    private void PerformActionOnBlockTable(IDraw drawer)
    {
        using (DocumentLock lockedDocument = Application.DocumentManager.MdiActiveDocument.LockDocument())
        {
            using (Database database = Application.DocumentManager.MdiActiveDocument.Database)
            {
                using (Transaction transaction = database.TransactionManager.StartTransaction())//Start the transaction
                {
                    //Open the block table for read
                    BlockTable blockTable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;

                    //Open the block table record model space for write
                    BlockTableRecord blockTableRecord = (BlockTableRecord)transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    DBObject newObject = drawer.DrawObject(blockTableRecord);

                    transaction.AddNewlyCreatedDBObject(newObject, true);

                    transaction.Commit();
                }
            }
        }
    }

注入Func<>:这似乎给了我类似的结果,具有更好的可读性。

[CommandMethod("Test", CommandFlags.Session)]
    public void Test()
    {
        PerformActionOnBlockTable(AddLineToDrawing);
        PerformActionOnBlockTable(AddCircleToDrawing);
    }

    private void PerformActionOnBlockTable(Func<BlockTableRecord, DBObject> action)
    {
        using (DocumentLock lockedDocument = Application.DocumentManager.MdiActiveDocument.LockDocument())
        {
            using (Database database = Application.DocumentManager.MdiActiveDocument.Database)
            {
                using (Transaction transaction = database.TransactionManager.StartTransaction())//Start the transaction
                {
                    //Open the block table for read
                    BlockTable blockTable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;

                    //Open the block table record model space for write
                    BlockTableRecord blockTableRecord = (BlockTableRecord)transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                    DBObject newObject = action(blockTableRecord);

                    transaction.AddNewlyCreatedDBObject(newObject, true);

                    transaction.Commit();
                }
            }
        }
    }

    private DBObject AddLineToDrawing(BlockTableRecord blockTableRecord)
    {
        Line line = new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0));
        blockTableRecord.AppendEntity(line);

        return line;
    }

    private DBObject AddCircleToDrawing(BlockTableRecord blockTableRecord)
    {
        Circle circle = new Circle(new Point3d(0, 0, 0), new Vector3d(0, 0, 0), 10);
        blockTableRecord.AppendEntity(circle);

        return circle;
    }

我可以诚实地说,我对DI没有做太多,所以我对此很陌生。你们中任何一位经验丰富的开发人员可以给我两种不同方法的Pro / Con吗?最后一种方法中有什么东西是红旗吗?它似乎比第二种方法更具可读性。也许我甚至都不完全理解注射...提前感谢您的投入!

2 个答案:

答案 0 :(得分:5)

你可以做一个简单的重构而不是你提供的选项:

[CommandMethod("Test", CommandFlags.Session)]   
public void Test() {   
  AddLineToDrawing();   
  AddCircleToDrawing();   
}  

private void AddLineToDrawing() {   
  CreateObjectOnBlockTable(
    new Line(new Point3d(0, 0, 0), new Point3d(10, 10, 0)));   
}   

private void AddCircleToDrawing() {   
  CreateObjectOnBlockTable(
    new Circle(new Point3d(0, 0, 0), new Vector3d(0, 0, 0), 10));   
}   

private void CreateObjectOnBlockTable(DBObject dbObject) { 
  using (var lockedDocument = Application.DocumentManager.MdiActiveDocument.LockDocument()) 
  using (var database = Application.DocumentManager.MdiActiveDocument.Database) 
  using (var transaction = database.TransactionManager.StartTransaction()) {
    // Open the block table for read 
    var blockTable = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead); 

    // Open the block table record model space for write 
    var blockTableRecord = (BlockTableRecord)transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite); 

    blockTableRecord.AppendEntity(dbObject); 
    transaction.AddNewlyCreatedDBObject(dbObject, true); 
    transaction.Commit(); 
  } 
} 

我认为这更具可读性。

UPDATE:要运行特殊逻辑,我喜欢使用委托的想法。我会像这样重构代码:

private void CreateObjectOnBlockTable(DBObject dbObject) {
  PerformActionOnBlockTable((transaction, blockTableRecord) => {
    blockTableRecord.AppendEntity(dbObject);  
    transaction.AddNewlyCreatedDBObject(dbObject, true);    
  });
}

private void PerformActionOnBlockTable(Action<Transaction, BlockTableRecord> action) {  
  using (var lockedDocument = Application.DocumentManager.MdiActiveDocument.LockDocument())  
  using (var database = Application.DocumentManager.MdiActiveDocument.Database)  
  using (var transaction = database.TransactionManager.StartTransaction()) { 
    // Open the block table for read  
    var blockTable = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead);  

    // Open the block table record model space for write  
    var blockTableRecord = (BlockTableRecord)transaction.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite);  

    // Run specific logic
    action(transaction, blockTableRecord);

    transaction.Commit();  
  }  
}  

(其余代码将是相同的)

可以重用

PerformActionOnBlockTable来使用事务和块表记录来运行任意逻辑。

答案 1 :(得分:1)

有人可能会争辩说,你的两个注射例子实际上是完全相同的 - 在PerformAction...方法中发送一个接口或一个委托是无关紧要的,而且真的是一个品味问题。话虽这么说,形状的单独类实现将允许PerformAction...类为open for extension but closed for modification。你的所有形状都是扩展,你的PerformAction...方法永远不需要改变。

具有讽刺意味的是,形状为the canonical example of the Open/Closed Principle