本人希望能够编写卓越的代码。

美观的代码就好像一篇小说,易懂易读,而且看起来非常美丽。在《代码之美》一书中,收音和录音了Ruby之父松本行宏的一篇小说,名为《把代码当作小说》,大概表明了一如既往的意义。Thoughtworks的壹位工程师在《软件开发沉思录》一书中建议,每一个类的法子最好永不超过5行。最初让本人感到很奇怪,继而觉得不也许。纵然那位工程师言辞凿凿,提到在融洽参加的品类中,全数代码都统统根据了这一规范,作者依旧表示疑虑。近日,阅读了RobertC. Martin的编写《代码整洁之道》(英文版名为Clean Code),看到Uncle
鲍勃演示的代码,真是了不起极了。仔细一看,那些好的代码在各样方法中山大学部都尚未当先5行。诀窍在何地?那就是重构手法中最常用的Extract
Method。进一步讲,倘使大家能够为种种类与办法以及变量定义出好的名字,代码确实能够成为一篇随笔。当然,是英文小说。

前日,小编在重温.NET的类别化时,在MSDN上找到一篇演示Xml连串化的以身作则代码。只怕是因为示范代码的原由,这一段代码写得极其地不优雅,甚至显得有些丑陋:

public class Test {
    public static void Main() {
        // Read and write purchase
orders.
        Test t = new Test();
        t.CreatePO(“po.xml”);
        t.ReadPO(“po.xml”);
    }

    private void CreatePO(string filename) {
        // Create an instance of the
XmlSerializer class;
        // specify the type of object to
serialize.
        XmlSerializer serializer =
        new XmlSerializer(typeof(PurchaseOrder));
        TextWriter writer = new
StreamWriter(filename);
        PurchaseOrder po = new PurchaseOrder();

        // Create an address to ship and
bill to.
        Address billAddress = new Address();
        billAddress.Name = “Teresa
Atkinson”;
        billAddress.Line1 = “1 Main
St.”;
        billAddress.City = “AnyTown”;
        billAddress.State = “WA”;
        billAddress.Zip = “00000”;
        // Set ShipTo and BillTo to the same
addressee.
        po.ShipTo = billAddress;
        po.OrderDate = System.DateTime.Now.ToLongDateString();

        // Create an OrderedItem
object.
        OrderedItem i1 = new OrderedItem();
        i1.ItemName = “Widget S”;
        i1.Description = “Small
widget”;
        i1.UnitPrice = (decimal)5.23;
        i1.Quantity = 3;
        i1.Calculate();

        // Insert the item into the
array.
        OrderedItem[] items = { i1
};
        po.OrderedItems = items;
        // Calculate the total
cost.
        decimal subTotal = new decimal();
        foreach (OrderedItem oi in items) {
            subTotal += oi.LineTotal;
        }
        po.SubTotal = subTotal;
        po.ShipCost = (decimal)12.51;
        po.TotalCost = po.SubTotal + po.ShipCost;
        // Serialize the purchase order, and
close the TextWriter.
        serializer.Serialize(writer, po);
        writer.Close();
    }

    protected void ReadPO(string filename) {
        // Create an instance of the
XmlSerializer class;
        // specify the type of object to be
deserialized.
        XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
        /* If the XML document has been
altered with unknown
        nodes or attributes, handle them
with the
        UnknownNode and UnknownAttribute
events.*/
        serializer.UnknownNode += new
        XmlNodeEventHandler(serializer_UnknownNode);
        serializer.UnknownAttribute += new
        XmlAttributeEventHandler(serializer_UnknownAttribute);

        // A FileStream is needed to read
the XML document.
        FileStream fs = new
FileStream(filename, FileMode.Open);
        // Declare an object variable of the
type to be deserialized.
        PurchaseOrder po;
        /* Use the Deserialize method to
restore the object’s state with
        data from the XML document.
*/
        po = (PurchaseOrder)serializer.Deserialize(fs);
        // Read the order date.
        Console.WriteLine(“OrderDate: ” + po.OrderDate);

        // Read the shipping
address.
        Address shipTo = po.ShipTo;
        ReadAddress(shipTo, “Ship
To:”);
        // Read the list of ordered
items.
        OrderedItem[] items =
po.OrderedItems;
        Console.WriteLine(“Items to be shipped:”);
        foreach (OrderedItem oi in items) {
            Console.WriteLine(“\t” +
            oi.ItemName + “\t” +
            oi.Description + “\t”

  •             oi.UnitPrice + “\t” +
                oi.Quantity + “\t” +
                oi.LineTotal);
            }
            // Read the subtotal, shipping cost,
    and total cost.
            Console.WriteLine(“\t\t\t\t\t Subtotal\t” +
    po.SubTotal);
            Console.WriteLine(“\t\t\t\t\t Shipping\t” +
    po.ShipCost);
            Console.WriteLine(“\t\t\t\t\t Total\t\t” +
    po.TotalCost);
        }

    protected void ReadAddress(Address a, string label) {
        // Read the fields of the Address
object.
        Console.WriteLine(label);
        Console.WriteLine(“\t” + a.Name);
        Console.WriteLine(“\t” + a.Line1);
        Console.WriteLine(“\t” + a.City);
        Console.WriteLine(“\t” + a.State);
        Console.WriteLine(“\t” + a.Zip);
        Console.WriteLine();
    }

    private void serializer_UnknownNode
    (object sender, XmlNodeEventArgs
e) {
        Console.WriteLine(“Unknown Node:” + e.Name + “\t” + e.Text);
    }

    private void serializer_UnknownAttribute
    (object sender,
XmlAttributeEventArgs e) {
        System.Xml.XmlAttribute attr
= e.Attr;
        Console.WriteLine(“Unknown attribute ” +
        attr.Name + “='” + attr.Value

  • “‘”);
        }
    }

看望CreatePO()和ReadPO(),多么地冗长。就算这么些完毕极为简约,但对此代码的阅读者而言,想要一下子引发该方法的着力思想,还是比较辛苦。别的,方法中的注释也体现多余,因为,代码自己就可以授予很好的印证。

下面,是小编对那段代码的重构,我们可以相比相比较,是还是不是越发便于阅读吧?

    public static class PurchaseOrderHandler {
        public static void CreatePurchaseOrder(string filename) {
            PurchaseOrder po =
BuildPurchaseOrder();
            XmlSerializer serializer
= new XmlSerializer(typeof(PurchaseOrder));
            using (var writer = new StreamWriter(filename)) {
                serializer.Serialize(writer, po);
            }
        }

        private static PurchaseOrder BuildPurchaseOrder() {
            Address address =
CreateAddress();
            OrderedItem i1 =
CreateOrderedItem();
            OrderedItem[] items = {
i1 };

            PurchaseOrder po = new PurchaseOrder();
            po.ShipTo = address;
            po.OrderDate = System.DateTime.Now.ToLongDateString();
            po.OrderedItems = items;
            po.SubTotal = CalculateSubTotal(items);
            po.ShipCost = (decimal)12.51;
            po.TotalCost = po.SubTotal + po.ShipCost;

            return po;
        }

        private static decimal CalculateSubTotal(OrderedItem[] items) {
            decimal subTotal = new decimal();
            foreach (OrderedItem oi in items) {
                subTotal += oi.LineTotal;
            }

            return subTotal;
        }

        private static OrderedItem CreateOrderedItem() {
            OrderedItem i1 = new OrderedItem();
            i1.ItemName = “Widget
S”;
            i1.Description = “Small
widget”;
            i1.UnitPrice = (decimal)5.23;
            i1.Quantity = 3;
            i1.Calculate();
            return i1;
        }

        private static Address CreateAddress() {
            Address billAddress =
new Address();
            billAddress.Name = “Bruce
Zhang”;
            billAddress.Line1 = “1 Main
St.”;
            billAddress.City = “Chong
Qing”;
            billAddress.State = “Chong
Qing”;
            billAddress.Zip = “400000”;

            return billAddress;
        }

        public static void ReadPurchaseOrder(string filename) {
            XmlSerializer serializer
= new XmlSerializer(typeof(PurchaseOrder));

            serializer.UnknownNode += new XmlNodeEventHandler(serializer_UnknownNode);
            serializer.UnknownAttribute += new XmlAttributeEventHandler(serializer_UnknownAttribute);

            FileStream fs = new FileStream(filename, FileMode.Open);

            PurchaseOrder po;
            po = (PurchaseOrder)serializer.Deserialize(fs);
            PurchaseOrderPrinter.PrintPurchaseOrder(po);
        }

        private static void serializer_UnknownNode
        (object sender, XmlNodeEventArgs e) {
            Console.WriteLine(“Unknown Node:” + e.Name + “\t” + e.Text);
        }

        private static void serializer_UnknownAttribute
        (object sender, XmlAttributeEventArgs e) {
            System.Xml.XmlAttribute
attr = e.Attr;
            Console.WriteLine(“Unknown attribute ” +
            attr.Name + “='” +
attr.Value + “‘”);
        }

        private static class PurchaseOrderPrinter {
            public static void PrintPurchaseOrder(PurchaseOrder po) {
                PrintOrderDate(po);
                PrintAddress(po.ShipTo);
                PrintOrderedItem(po.OrderedItems);
                PrintOrderCost(po);
            }

            private static void PrintOrderCost(PurchaseOrder po) {
                Console.WriteLine(“\t\t\t\t\t Subtotal\t” +
po.SubTotal);
                Console.WriteLine(“\t\t\t\t\t Shipping\t” +
po.ShipCost);
                Console.WriteLine(“\t\t\t\t\t Total\t\t” +
po.TotalCost);
            }

            private static void PrintOrderDate(PurchaseOrder po) {
                Console.WriteLine(“OrderDate: ” + po.OrderDate);
            }

            private static void PrintOrderedItem(OrderedItem[] items) {
                Console.WriteLine(“Items to be shipped:”);
                foreach (OrderedItem oi in items) {
                    Console.WriteLine(“\t” +
                    oi.ItemName + “\t” +
                    oi.Description + “\t” +
                    oi.UnitPrice + “\t” +
                    oi.Quantity + “\t” +
                    oi.LineTotal);
                }
            }

            private static void PrintAddress(Address a) {
                // Read the fields of the
Address object.
                Console.WriteLine(“Ship To:”);
                Console.WriteLine(“\t” + a.Name);
                Console.WriteLine(“\t” + a.Line1);
                Console.WriteLine(“\t” + a.City);
                Console.WriteLine(“\t” + a.State);
                Console.WriteLine(“\t” + a.Zip);
                Console.WriteLine();
            }
        }
    }

阅读代码时,大家得以先关心最要紧的格局,即CreatePurchaseOrder()和ReadPurchaseOrder()方法。要是并不期待精晓过多组织PO对象的细节,通过阅读那样归纳的办法,能够很不难地抓住那五个办法的贯彻,这就是透过营造二个PO对象,进行系列化,而在反体系化时,将获取的PO对象新闻打字与印刷出来。

实质上,不佳的代码不必然正是初大方的“专利”,让我们看看NHibernate中的一段代码:

public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
{
    Init();
    log.Info(“building session
factory”);

    properties = new Dictionary<string, string>(cfg.Properties);
    interceptor = cfg.Interceptor;
    this.settings = settings;
    sqlFunctionRegistry = new SQLFunctionRegistry(settings.Dialect,
cfg.SqlFunctions);
    eventListeners = listeners;
    filters = new Dictionary<string, FilterDefinition>(cfg.FilterDefinitions);
    if (log.IsDebugEnabled)
    {
        log.Debug(“Session factory
constructed with filter configurations : ” + CollectionPrinter.ToString(filters));
    }

    if (log.IsDebugEnabled)
    {
        log.Debug(“instantiating session
factory with properties: ” + CollectionPrinter.ToString(properties));
    }

    try
    {
        if
(settings.IsKeywordsImportEnabled)
        {
            SchemaMetadataUpdater.Update(this);
        }
        if
(settings.IsAutoQuoteEnabled)
        {
            SchemaMetadataUpdater.QuoteTableAndColumns(cfg);
        }
    }
    catch (NotSupportedException)
    {
        // Ignore if the Dialect does not
provide DataBaseSchema
    }

    #region Caches
    settings.CacheProvider.Start(properties);
    #endregion

    #region Generators
    identifierGenerators = new Dictionary<string, IIdentifierGenerator>();
    foreach (PersistentClass model in cfg.ClassMappings)
    {
        if (!model.IsInherited)
        {
            IIdentifierGenerator
generator =
                model.Identifier.CreateIdentifierGenerator(settings.Dialect,
settings.DefaultCatalogName,
                                                           settings.DefaultSchemaName,
(RootClass) model);

            identifierGenerators[model.EntityName] = generator;
        }
    }
    #endregion

    #region Persisters

    Dictionary<string, ICacheConcurrencyStrategy> caches =
new Dictionary<string, ICacheConcurrencyStrategy>();
    entityPersisters = new Dictionary<string, IEntityPersister>();
    implementorToEntityName = new
Dictionary<System.Type, string>();

    Dictionary<string, IClassMetadata> classMeta = new Dictionary<string, IClassMetadata>();

    foreach (PersistentClass model in cfg.ClassMappings)
    {
        model.PrepareTemporaryTables(mapping, settings.Dialect);
        string cacheRegion =
model.RootClazz.CacheRegionName;
        ICacheConcurrencyStrategy
cache;
        if
(!caches.TryGetValue(cacheRegion, out
cache))
        {
            cache =
                CacheFactory.CreateCache(model.CacheConcurrencyStrategy,
cacheRegion, model.IsMutable, settings, properties);
            if (cache != null)
            {
                caches.Add(cacheRegion, cache);
                allCacheRegions.Add(cache.RegionName, cache.Cache);
            }
        }
        IEntityPersister cp = PersisterFactory.CreateClassPersister(model,
cache, this, mapping);
        entityPersisters[model.EntityName] = cp;
        classMeta[model.EntityName] = cp.ClassMetadata;

        if
(model.HasPocoRepresentation)
        {
            implementorToEntityName[model.MappedClass] =
model.EntityName;
        }
    }
    classMetadata = new UnmodifiableDictionary<string, IClassMetadata>(classMeta);

    Dictionary<string, ISet<string>> tmpEntityToCollectionRoleMap
= new Dictionary<string, ISet<string>>();
    collectionPersisters = new Dictionary<string, ICollectionPersister>();
    foreach (Mapping.Collection model in cfg.CollectionMappings)
    {
        ICacheConcurrencyStrategy
cache =
            CacheFactory.CreateCache(model.CacheConcurrencyStrategy,
model.CacheRegionName, model.Owner.IsMutable, settings,
                                     properties);
        if (cache != null)
        {
            allCacheRegions[cache.RegionName] = cache.Cache;
        }
        ICollectionPersister
persister = PersisterFactory.CreateCollectionPersister(cfg,
model, cache, this);
        collectionPersisters[model.Role] = persister;
        IType indexType =
persister.IndexType;
        if (indexType != null && indexType.IsAssociationType &&
!indexType.IsAnyType)
        {
            string entityName =
((IAssociationType)
indexType).GetAssociatedEntityName(this);
            ISet<string> roles;
            if
(!tmpEntityToCollectionRoleMap.TryGetValue(entityName, out roles))
            {
                roles = new HashedSet<string>();
                tmpEntityToCollectionRoleMap[entityName] = roles;
            }
            roles.Add(persister.Role);
        }
        IType elementType =
persister.ElementType;
        if
(elementType.IsAssociationType && !elementType.IsAnyType)
        {
            string entityName =
((IAssociationType)
elementType).GetAssociatedEntityName(this);
            ISet<string> roles;
            if
(!tmpEntityToCollectionRoleMap.TryGetValue(entityName, out roles))
            {
                roles = new HashedSet<string>();
                tmpEntityToCollectionRoleMap[entityName] = roles;
            }
            roles.Add(persister.Role);
        }
    }
    Dictionary<string, ICollectionMetadata>
tmpcollectionMetadata = new Dictionary<string, ICollectionMetadata>(collectionPersisters.Count);
    foreach (KeyValuePair<string, ICollectionPersister>
collectionPersister in
collectionPersisters)
    {
        tmpcollectionMetadata.Add(collectionPersister.Key,
collectionPersister.Value.CollectionMetadata);
    }
    collectionMetadata = new UnmodifiableDictionary<string, ICollectionMetadata>(tmpcollectionMetadata);
    collectionRolesByEntityParticipant = new UnmodifiableDictionary<string, ISet<string>>(tmpEntityToCollectionRoleMap);
    #endregion

    #region Named Queries
    namedQueries = new Dictionary<string, NamedQueryDefinition>(cfg.NamedQueries);
    namedSqlQueries = new Dictionary<string, NamedSQLQueryDefinition>(cfg.NamedSQLQueries);
    sqlResultSetMappings = new Dictionary<string, ResultSetMappingDefinition>(cfg.SqlResultSetMappings);
    #endregion

    imports = new Dictionary<string, string>(cfg.Imports);

    #region after *all* persisters
and named queries are registered
    foreach (IEntityPersister persister in entityPersisters.Values)
    {
        persister.PostInstantiate();
    }
    foreach (ICollectionPersister persister in collectionPersisters.Values)
    {
        persister.PostInstantiate();
    }
    #endregion

    #region Serialization info

    name = settings.SessionFactoryName;
    try
    {
        uuid = (string)
UuidGenerator.Generate(null, null);
    }
    catch (Exception)
    {
        throw new AssertionFailure(“Could not generate UUID”);
    }

    SessionFactoryObjectFactory.AddInstance(uuid,
name, this, properties);

    #endregion

    log.Debug(“Instantiated session
factory”);

    #region Schema management
    if
(settings.IsAutoCreateSchema)
    {
        new SchemaExport(cfg).Create(false, true);
    }

    if ( settings.IsAutoUpdateSchema
)
    {
        new SchemaUpdate(cfg).Execute(false, true);
    }
    if
(settings.IsAutoValidateSchema)
    {
         new SchemaValidator(cfg,
settings).Validate();
    }
    if (settings.IsAutoDropSchema)
    {
        schemaExport = new SchemaExport(cfg);
    }
    #endregion

    #region Obtaining
TransactionManager
    // not ported yet
    #endregion

    currentSessionContext = BuildCurrentSessionContext();

    if
(settings.IsQueryCacheEnabled)
    {
        updateTimestampsCache = new
UpdateTimestampsCache(settings,
properties);
        queryCache = settings.QueryCacheFactory.GetQueryCache(null, updateTimestampsCache, settings,
properties);
        queryCaches = new ThreadSafeDictionary<string, IQueryCache>(new Dictionary<string, IQueryCache>());
    }
    else
    {
        updateTimestampsCache = null;
        queryCache = null;
        queryCaches = null;
    }

    #region Checking for named
queries
    if
(settings.IsNamedQueryStartupCheckingEnabled)
    {
        IDictionary<string, HibernateException> errors =
CheckNamedQueries();
        if (errors.Count > 0)
        {
            StringBuilder
failingQueries = new StringBuilder(“Errors in named queries: “);
            foreach (KeyValuePair<string, HibernateException> pair in errors)
            {
                failingQueries.Append(‘{‘).Append(pair.Key).Append(‘}’);
                log.Error(“Error in named
query: ” + pair.Key, pair.Value);
            }
            throw new HibernateException(failingQueries.ToString());
        }
    }
    #endregion

    Statistics.IsStatisticsEnabled = settings.IsStatisticsEnabled;

    // EntityNotFoundDelegate
    IEntityNotFoundDelegate enfd =
cfg.EntityNotFoundDelegate;
    if (enfd == null)
    {
        enfd = new DefaultEntityNotFoundDelegate();
    }
    entityNotFoundDelegate = enfd;
}

那是类SessionFactoryImpl(它实现了ISessionFactoryImplementor接口)的构造函数,其指标时是透过Configuration以及Setting中的有些值,去伊始化SessionFactoryImpl,然后创设该类的靶子。坦白说,作者根本没有看过那样“浩瀚无垠”的构造函数。幸亏,Visual
Studio提升了Region,不然,更令人头疼。(作者在想,既然代码的编者已经运用了Region来划分达成,为啥不进一步将其分割为小的法门吧?)

看那样的代码,咱们能够自由读懂吗?

劣质代码可谓遗患无穷。在《程序员修炼之道》一书中,提到了所谓“破窗效应”,即“没修复的破窗,导致越多的窗牖被打破”。丑陋的代码若是唯有2个小的一对,看似鸡毛蒜皮,就如一幢大楼的一扇破窗一般不难令人遗忘。随着时间的推迟,当这一个丑陋代码不知不觉蔓延到整个项目中时,大家才发现这一幢大楼已经满目疮痍了。“一屋不扫,何以扫天下”,程序员应该从小处动手,现在才也许写出优雅的代码。

相关文章

网站地图xml地图