प्रक्रियात्मक से ऑब्जेक्ट ओरिएंटेड कोड में परिवर्तित करें


16

मैं विरासत कोड और स्वच्छ कोड के साथ प्रभावी ढंग से कार्य कर रहा हूं एक बड़े ASP.NET वेबफॉर्म एप्लिकेशन के मौजूदा कोड-बेस की सफाई कैसे शुरू करें, इस पर सीखने की रणनीतियों के लक्ष्य के साथ ।

यह प्रणाली 2005 के बाद से आस-पास रही है और तब से इसमें कई वृद्धि हुई है। मूल रूप से कोड को इस प्रकार संरचित किया गया था (और अभी भी काफी हद तक इस तरह से संरचित है):

  • ASP.NET (aspx / ascx)
  • कोड-पीछे (c #)
  • व्यापार तर्क परत (c #)
  • डेटा एक्सेस लेयर (c #)
  • डेटाबेस (Oracle)

मुख्य मुद्दा यह है कि कोड प्रक्रियात्मक वस्तु-उन्मुख वस्तु के रूप में है। यह वस्तुतः दोनों पुस्तकों में वर्णित दिशानिर्देशों का उल्लंघन करता है।

यह व्यापार तर्क परत में एक विशिष्ट वर्ग का एक उदाहरण है:

    public class AddressBO
{
    public TransferObject GetAddress(string addressID)
    {
        if (StringUtils.IsNull(addressID))
        {
            throw new ValidationException("Address ID must be entered");
        }

        AddressDAO addressDAO = new AddressDAO();
        return addressDAO.GetAddress(addressID);
    }

    public TransferObject Insert(TransferObject addressDetails)
    {
        if (StringUtils.IsNull(addressDetails.GetString("EVENT_ID")) ||
            StringUtils.IsNull(addressDetails.GetString("LOCALITY")) ||
            StringUtils.IsNull(addressDetails.GetString("ADDRESS_TARGET")) ||
            StringUtils.IsNull(addressDetails.GetString("ADDRESS_TYPE_CODE")) ||
            StringUtils.IsNull(addressDetails.GetString("CREATED_BY")))
        {
            throw new ValidationException(
                "You must enter an Event ID, Locality, Address Target, Address Type Code and Created By.");
        }

        string addressID = Sequence.GetNextValue("ADDRESS_ID_SEQ");
        addressDetails.SetValue("ADDRESS_ID", addressID);

        string syncID = Sequence.GetNextValue("SYNC_ID_SEQ");
        addressDetails.SetValue("SYNC_ADDRESS_ID", syncID);

        TransferObject syncDetails = new TransferObject();

        Transaction transaction = new Transaction();

        try
        {
            AddressDAO addressDAO = new AddressDAO();
            addressDAO.Insert(addressDetails, transaction);

            // insert the record for the target
            TransferObject addressTargetDetails = new TransferObject();
            switch (addressDetails.GetString("ADDRESS_TARGET"))
            {
                case "PARTY_ADDRESSES":
                    {
                        addressTargetDetails.SetValue("ADDRESS_ID", addressID);
                        addressTargetDetails.SetValue("ADDRESS_TYPE_CODE",
                                                      addressDetails.GetString("ADDRESS_TYPE_CODE"));
                        addressTargetDetails.SetValue("PARTY_ID", addressDetails.GetString("PARTY_ID"));
                        addressTargetDetails.SetValue("EVENT_ID", addressDetails.GetString("EVENT_ID"));
                        addressTargetDetails.SetValue("CREATED_BY", addressDetails.GetString("CREATED_BY"));

                        addressDAO.InsertPartyAddress(addressTargetDetails, transaction);

                        break;
                    }
                case "PARTY_CONTACT_ADDRESSES":
                    {
                        addressTargetDetails.SetValue("ADDRESS_ID", addressID);
                        addressTargetDetails.SetValue("ADDRESS_TYPE_CODE",
                                                      addressDetails.GetString("ADDRESS_TYPE_CODE"));
                        addressTargetDetails.SetValue("PUBLIC_RELEASE_FLAG",
                                                      addressDetails.GetString("PUBLIC_RELEASE_FLAG"));
                        addressTargetDetails.SetValue("CONTACT_ID", addressDetails.GetString("CONTACT_ID"));
                        addressTargetDetails.SetValue("EVENT_ID", addressDetails.GetString("EVENT_ID"));
                        addressTargetDetails.SetValue("CREATED_BY", addressDetails.GetString("CREATED_BY"));

                        addressDAO.InsertContactAddress(addressTargetDetails, transaction);

                        break;
                    }

                << many more cases here >>
                default:
                    {
                        break;
                    }
            }

            // synchronise
            SynchronisationBO synchronisationBO = new SynchronisationBO();
            syncDetails = synchronisationBO.Synchronise("I", transaction,
                                                        "ADDRESSES", addressDetails.GetString("ADDRESS_TARGET"),
                                                        addressDetails, addressTargetDetails);


            // commit
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
            throw;
        }

        return new TransferObject("ADDRESS_ID", addressID, "SYNC_DETAILS", syncDetails);
    }


    << many more methods are here >>

}

इसमें बहुत अधिक दोहराव है, वर्ग के पास कई जिम्मेदारियां हैं, आदि-आदि - यह आमतौर पर 'अन-क्लीन' कोड है।

पूरे सिस्टम में सभी कोड ठोस कार्यान्वयन पर निर्भर हैं।

यह डेटा एक्सेस लेयर में एक विशिष्ट वर्ग का एक उदाहरण है:

    public class AddressDAO : GenericDAO
{
    public static readonly string BASE_SQL_ADDRESSES =
        "SELECT " +
        "  a.address_id, " +
        "  a.event_id, " +
        "  a.flat_unit_type_code, " +
        "  fut.description as flat_unit_description, " +
        "  a.flat_unit_num, " +
        "  a.floor_level_code, " +
        "  fl.description as floor_level_description, " +
        "  a.floor_level_num, " +
        "  a.building_name, " +
        "  a.lot_number, " +
        "  a.street_number, " +
        "  a.street_name, " +
        "  a.street_type_code, " +
        "  st.description as street_type_description, " +
        "  a.street_suffix_code, " +
        "  ss.description as street_suffix_description, " +
        "  a.postal_delivery_type_code, " +
        "  pdt.description as postal_delivery_description, " +
        "  a.postal_delivery_num, " +
        "  a.locality, " +
        "  a.state_code, " +
        "  s.description as state_description, " +
        "  a.postcode, " +
        "  a.country, " +
        "  a.lock_num, " +
        "  a.created_by, " +
        "  TO_CHAR(a.created_datetime, '" + SQL_DATETIME_FORMAT + "') as created_datetime, " +
        "  a.last_updated_by, " +
        "  TO_CHAR(a.last_updated_datetime, '" + SQL_DATETIME_FORMAT + "') as last_updated_datetime, " +
        "  a.sync_address_id, " +
        "  a.lat," +
        "  a.lon, " +
        "  a.validation_confidence, " +
        "  a.validation_quality, " +
        "  a.validation_status " +
        "FROM ADDRESSES a, FLAT_UNIT_TYPES fut, FLOOR_LEVELS fl, STREET_TYPES st, " +
        "     STREET_SUFFIXES ss, POSTAL_DELIVERY_TYPES pdt, STATES s " +
        "WHERE a.flat_unit_type_code = fut.flat_unit_type_code(+) " +
        "AND   a.floor_level_code = fl.floor_level_code(+) " +
        "AND   a.street_type_code = st.street_type_code(+) " +
        "AND   a.street_suffix_code = ss.street_suffix_code(+) " +
        "AND   a.postal_delivery_type_code = pdt.postal_delivery_type_code(+) " +
        "AND   a.state_code = s.state_code(+) ";


    public TransferObject GetAddress(string addressID)
    {
        //Build the SELECT Statement
        StringBuilder selectStatement = new StringBuilder(BASE_SQL_ADDRESSES);

        //Add WHERE condition
        selectStatement.Append(" AND a.address_id = :addressID");

        ArrayList parameters = new ArrayList{DBUtils.CreateOracleParameter("addressID", OracleDbType.Decimal, addressID)};

        // Execute the SELECT statement
        Query query = new Query();
        DataSet results = query.Execute(selectStatement.ToString(), parameters);

        // Check if 0 or more than one rows returned
        if (results.Tables[0].Rows.Count == 0)
        {
            throw new NoDataFoundException();
        }
        if (results.Tables[0].Rows.Count > 1)
        {
            throw new TooManyRowsException();
        }

        // Return a TransferObject containing the values
        return new TransferObject(results);
    }


    public void Insert(TransferObject insertValues, Transaction transaction)
    {
        // Store Values
        string addressID = insertValues.GetString("ADDRESS_ID");
        string syncAddressID = insertValues.GetString("SYNC_ADDRESS_ID");
        string eventID = insertValues.GetString("EVENT_ID");
        string createdBy = insertValues.GetString("CREATED_BY");

        // postal delivery
        string postalDeliveryTypeCode = insertValues.GetString("POSTAL_DELIVERY_TYPE_CODE");
        string postalDeliveryNum = insertValues.GetString("POSTAL_DELIVERY_NUM");

        // unit/building
        string flatUnitTypeCode = insertValues.GetString("FLAT_UNIT_TYPE_CODE");
        string flatUnitNum = insertValues.GetString("FLAT_UNIT_NUM");
        string floorLevelCode = insertValues.GetString("FLOOR_LEVEL_CODE");
        string floorLevelNum = insertValues.GetString("FLOOR_LEVEL_NUM");
        string buildingName = insertValues.GetString("BUILDING_NAME");

        // street
        string lotNumber = insertValues.GetString("LOT_NUMBER");
        string streetNumber = insertValues.GetString("STREET_NUMBER");
        string streetName = insertValues.GetString("STREET_NAME");
        string streetTypeCode = insertValues.GetString("STREET_TYPE_CODE");
        string streetSuffixCode = insertValues.GetString("STREET_SUFFIX_CODE");

        // locality/state/postcode/country
        string locality = insertValues.GetString("LOCALITY");
        string stateCode = insertValues.GetString("STATE_CODE");
        string postcode = insertValues.GetString("POSTCODE");
        string country = insertValues.GetString("COUNTRY");

        // esms address
        string esmsAddress = insertValues.GetString("ESMS_ADDRESS");

        //address/GPS
        string lat = insertValues.GetString("LAT");
        string lon = insertValues.GetString("LON");
        string zoom = insertValues.GetString("ZOOM");

        //string validateDate = insertValues.GetString("VALIDATED_DATE");
        string validatedBy = insertValues.GetString("VALIDATED_BY");
        string confidence = insertValues.GetString("VALIDATION_CONFIDENCE");
        string status = insertValues.GetString("VALIDATION_STATUS");
        string quality = insertValues.GetString("VALIDATION_QUALITY");


        // the insert statement
        StringBuilder insertStatement = new StringBuilder("INSERT INTO ADDRESSES (");
        StringBuilder valuesStatement = new StringBuilder("VALUES (");

        ArrayList parameters = new ArrayList();

        // build the insert statement
        insertStatement.Append("ADDRESS_ID, EVENT_ID, CREATED_BY, CREATED_DATETIME, LOCK_NUM ");
        valuesStatement.Append(":addressID, :eventID, :createdBy, SYSDATE, 1 ");
        parameters.Add(DBUtils.CreateOracleParameter("addressID", OracleDbType.Decimal, addressID));
        parameters.Add(DBUtils.CreateOracleParameter("eventID", OracleDbType.Decimal, eventID));
        parameters.Add(DBUtils.CreateOracleParameter("createdBy", OracleDbType.Varchar2, createdBy));

        // build the insert statement
        if (!StringUtils.IsNull(syncAddressID))
        {
            insertStatement.Append(", SYNC_ADDRESS_ID");
            valuesStatement.Append(", :syncAddressID");
            parameters.Add(DBUtils.CreateOracleParameter("syncAddressID", OracleDbType.Decimal, syncAddressID));
        }

        if (!StringUtils.IsNull(postalDeliveryTypeCode))
        {
            insertStatement.Append(", POSTAL_DELIVERY_TYPE_CODE");
            valuesStatement.Append(", :postalDeliveryTypeCode ");
            parameters.Add(DBUtils.CreateOracleParameter("postalDeliveryTypeCode", OracleDbType.Varchar2, postalDeliveryTypeCode));
        }

        if (!StringUtils.IsNull(postalDeliveryNum))
        {
            insertStatement.Append(", POSTAL_DELIVERY_NUM");
            valuesStatement.Append(", :postalDeliveryNum ");
            parameters.Add(DBUtils.CreateOracleParameter("postalDeliveryNum", OracleDbType.Varchar2, postalDeliveryNum));
        }

        if (!StringUtils.IsNull(flatUnitTypeCode))
        {
            insertStatement.Append(", FLAT_UNIT_TYPE_CODE");
            valuesStatement.Append(", :flatUnitTypeCode ");
            parameters.Add(DBUtils.CreateOracleParameter("flatUnitTypeCode", OracleDbType.Varchar2, flatUnitTypeCode));
        }

        if (!StringUtils.IsNull(lat))
        {
            insertStatement.Append(", LAT");
            valuesStatement.Append(", :lat ");
            parameters.Add(DBUtils.CreateOracleParameter("lat", OracleDbType.Decimal, lat));
        }

        if (!StringUtils.IsNull(lon))
        {
            insertStatement.Append(", LON");
            valuesStatement.Append(", :lon ");
            parameters.Add(DBUtils.CreateOracleParameter("lon", OracleDbType.Decimal, lon));
        }

        if (!StringUtils.IsNull(zoom))
        {
            insertStatement.Append(", ZOOM");
            valuesStatement.Append(", :zoom ");
            parameters.Add(DBUtils.CreateOracleParameter("zoom", OracleDbType.Decimal, zoom));
        }

        if (!StringUtils.IsNull(flatUnitNum))
        {
            insertStatement.Append(", FLAT_UNIT_NUM");
            valuesStatement.Append(", :flatUnitNum ");
            parameters.Add(DBUtils.CreateOracleParameter("flatUnitNum", OracleDbType.Varchar2, flatUnitNum));
        }

        if (!StringUtils.IsNull(floorLevelCode))
        {
            insertStatement.Append(", FLOOR_LEVEL_CODE");
            valuesStatement.Append(", :floorLevelCode ");
            parameters.Add(DBUtils.CreateOracleParameter("floorLevelCode", OracleDbType.Varchar2, floorLevelCode));
        }

        if (!StringUtils.IsNull(floorLevelNum))
        {
            insertStatement.Append(", FLOOR_LEVEL_NUM");
            valuesStatement.Append(", :floorLevelNum ");
            parameters.Add(DBUtils.CreateOracleParameter("floorLevelNum", OracleDbType.Varchar2, floorLevelNum));
        }

        if (!StringUtils.IsNull(buildingName))
        {
            insertStatement.Append(", BUILDING_NAME");
            valuesStatement.Append(", :buildingName ");
            parameters.Add(DBUtils.CreateOracleParameter("buildingName", OracleDbType.Varchar2, buildingName));
        }

        if (!StringUtils.IsNull(lotNumber))
        {
            insertStatement.Append(", LOT_NUMBER");
            valuesStatement.Append(", :lotNumber ");
            parameters.Add(DBUtils.CreateOracleParameter("lotNumber", OracleDbType.Varchar2, lotNumber));
        }

        if (!StringUtils.IsNull(streetNumber))
        {
            insertStatement.Append(", STREET_NUMBER");
            valuesStatement.Append(", :streetNumber ");
            parameters.Add(DBUtils.CreateOracleParameter("streetNumber", OracleDbType.Varchar2, streetNumber));
        }

        if (!StringUtils.IsNull(streetName))
        {
            insertStatement.Append(", STREET_NAME");
            valuesStatement.Append(", :streetName ");
            parameters.Add(DBUtils.CreateOracleParameter("streetName", OracleDbType.Varchar2, streetName));
        }

        if (!StringUtils.IsNull(streetTypeCode))
        {
            insertStatement.Append(", STREET_TYPE_CODE");
            valuesStatement.Append(", :streetTypeCode ");
            parameters.Add(DBUtils.CreateOracleParameter("streetTypeCode", OracleDbType.Varchar2, streetTypeCode));
        }

        if (!StringUtils.IsNull(streetSuffixCode))
        {
            insertStatement.Append(", STREET_SUFFIX_CODE");
            valuesStatement.Append(", :streetSuffixCode ");
            parameters.Add(DBUtils.CreateOracleParameter("streetSuffixCode", OracleDbType.Varchar2, streetSuffixCode));
        }

        if (!StringUtils.IsNull(locality))
        {
            insertStatement.Append(", LOCALITY");
            valuesStatement.Append(", :locality");
            parameters.Add(DBUtils.CreateOracleParameter("locality", OracleDbType.Varchar2, locality));
        }

        if (!StringUtils.IsNull(stateCode))
        {
            insertStatement.Append(", STATE_CODE");
            valuesStatement.Append(", :stateCode");
            parameters.Add(DBUtils.CreateOracleParameter("stateCode", OracleDbType.Varchar2, stateCode));
        }

        if (!StringUtils.IsNull(postcode))
        {
            insertStatement.Append(", POSTCODE");
            valuesStatement.Append(", :postcode ");
            parameters.Add(DBUtils.CreateOracleParameter("postcode", OracleDbType.Varchar2, postcode));
        }

        if (!StringUtils.IsNull(country))
        {
            insertStatement.Append(", COUNTRY");
            valuesStatement.Append(", :country ");
            parameters.Add(DBUtils.CreateOracleParameter("country", OracleDbType.Varchar2, country));
        }

        if (!StringUtils.IsNull(esmsAddress))
        {
            insertStatement.Append(", ESMS_ADDRESS");
            valuesStatement.Append(", :esmsAddress ");
            parameters.Add(DBUtils.CreateOracleParameter("esmsAddress", OracleDbType.Varchar2, esmsAddress));
        }

        if (!StringUtils.IsNull(validatedBy))
        {
            insertStatement.Append(", VALIDATED_DATE");
            valuesStatement.Append(", SYSDATE ");
            insertStatement.Append(", VALIDATED_BY");
            valuesStatement.Append(", :validatedBy ");
            parameters.Add(DBUtils.CreateOracleParameter("validatedBy", OracleDbType.Varchar2, validatedBy));
        }


        if (!StringUtils.IsNull(confidence))
        {
            insertStatement.Append(", VALIDATION_CONFIDENCE");
            valuesStatement.Append(", :confidence ");
            parameters.Add(DBUtils.CreateOracleParameter("confidence", OracleDbType.Decimal, confidence));
        }

        if (!StringUtils.IsNull(status))
        {
            insertStatement.Append(", VALIDATION_STATUS");
            valuesStatement.Append(", :status ");
            parameters.Add(DBUtils.CreateOracleParameter("status", OracleDbType.Varchar2, status));
        }

        if (!StringUtils.IsNull(quality))
        {
            insertStatement.Append(", VALIDATION_QUALITY");
            valuesStatement.Append(", :quality ");
            parameters.Add(DBUtils.CreateOracleParameter("quality", OracleDbType.Decimal, quality));
        }

        // finish off the statement
        insertStatement.Append(") ");
        valuesStatement.Append(")");

        // build the insert statement
        string sql = insertStatement + valuesStatement.ToString();

        // Execute the INSERT Statement
        Dml dmlDAO = new Dml();
        int rowsAffected = dmlDAO.Execute(sql, transaction, parameters);

        if (rowsAffected == 0)
        {
            throw new NoRowsAffectedException();
        }
    }

    << many more methods go here >>
}

इस प्रणाली को मेरे द्वारा और एक छोटी सी टीम द्वारा 2005 में 1 सप्ताह के .NET पाठ्यक्रम के बाद विकसित किया गया था। इससे पहले कि मेरा अनुभव क्लाइंट-सर्वर अनुप्रयोगों में था। पिछले 5 वर्षों में मैं स्वचालित इकाई परीक्षण, स्वचालित एकीकरण परीक्षण और स्वचालित स्वीकृति परीक्षण (सेलेनियम या समकक्ष का उपयोग करके) के लाभों को पहचानने के लिए आया हूं, लेकिन वर्तमान कोड-आधार इन अवधारणाओं को पेश करना असंभव लगता है।

हम अब तंग समय-फ्रेम के साथ एक प्रमुख वृद्धि परियोजना पर काम करना शुरू कर रहे हैं। टीम में 5 .NET डेवलपर्स शामिल हैं - 2 डेवलपर्स कुछ वर्षों के .NET अनुभव के साथ और 3 अन्य जिनके पास बहुत कम या कोई .NET अनुभव नहीं है। किसी भी टीम (स्वयं सहित) को .NET यूनिट टेस्टिंग या मॉकिंग फ्रेमवर्क का उपयोग करने का अनुभव नहीं है।

इस कोड को क्लीनर, अधिक ऑब्जेक्ट-ओरिएंटेड, परीक्षण योग्य और बनाए रखने के लिए आप किस रणनीति का उपयोग करेंगे?


9
एक तरफ के रूप में, यह दोहरी जांच के लायक हो सकता है कि सिस्टम को फिर से लिखने के लिए एक लागत औचित्य है। पुराना कोड बदसूरत हो सकता है, लेकिन अगर यह पर्याप्त रूप से काम करता है तो खुरदुरे किनारों के साथ रखना सस्ता हो सकता है और आपके विकास के समय को कहीं और लगाया जा सकता है।
स्मिथको

प्रत्येक वृद्धि परियोजना के बाद मैन्युअल पुन: परीक्षण के प्रयास और लागत को कम करना एक उचित औचित्य है। अंतिम परियोजना के अंत में, मैनुअल परीक्षण लगभग 2 महीने के लिए चला गया। यदि अधिक स्वचालित परीक्षण की शुरूआत इस प्रयास को 1-2 सप्ताह तक कम कर देती है तो यह इसके लायक हो सकता है।
एंथनी

5
कानूनी कोड के लिए, यह अच्छा है अच्छा है!
जॉब

मैं मानता हूँ कि यथोचित रूप से सुसंगत और संरचित है। मेरा मुख्य उद्देश्य परिवर्तन के दुष्प्रभावों को कम करना है। प्रत्येक प्रोजेक्ट बड़े पैमाने पर (और उसके बाद) पूरे मैन्युअल रूप से परीक्षण करने के लिए आवश्यक प्रयास। मैंने सेलेनियम का उपयोग करने के बारे में क्लाइंट-साइड के माध्यम से परीक्षण करने के बारे में सोचा था - मेरे पास सर्वरफ़ॉल्ट ( serverfault.com/questions/236546/… ) पर एक सवाल है जो डेटाबेस को जल्दी से पुनः प्राप्त करने पर सुझाव प्राप्त करने के लिए है। मुझे लगता है कि स्वचालित स्वीकृति परीक्षण से बड़े पैमाने पर पुनर्लेखन करने के बिना अधिकांश लाभ मिलेंगे।
एंथनी

जवाबों:


16

आप दो पुस्तकों का उल्लेख करते हैं जिनमें से एक प्राथमिक संदेश है "द बॉय स्काउट नियम" अर्थात जैसे ही आप इसे छूते हैं, कोड को साफ करें। यदि आपके पास एक कार्य प्रणाली है, तो एक बड़े पैमाने पर फिर से लिखना काउंटर-उत्पादक है। इसके बजाय, जैसा कि आप नई कार्यक्षमता जोड़ते हैं, सुनिश्चित करें कि आप कोड में सुधार करते हैं क्योंकि यह खड़ा है।

  • मौजूदा कोड को कवर करने के लिए यूनिट टेस्ट लिखें जिन्हें आपको बदलना होगा।
  • उस कोड को रिफलेक्टर करें ताकि यह परिवर्तन के लिए अधिक उपयोगी हो (यह सुनिश्चित करना कि आपके परीक्षण अभी भी पास हैं)।
  • नई / संशोधित कार्यक्षमता के लिए परीक्षण लिखें
  • नए टेस्ट पास करने के लिए कोड लिखें
  • आवश्यकतानुसार रिफ्लेक्टर।

आगे गोता लगाने के लिए, पंख अपने सीम पर अनुप्रयोग के परीक्षण के बारे में बात करता है: तार्किक बिंदु जिस पर इकाइयां जुड़ती हैं। आप एक निर्भरता के लिए एक स्टब या मॉक बनाने के लिए सीम का लाभ उठा सकते हैं ताकि आप आश्रित वस्तु के चारों ओर परीक्षण लिख सकें। चलिए अपने AddressBO को एक उदाहरण के रूप में लेते हैं

public class AddressBO
{
    public TransferObject GetAddress(string addressID)
    {
        if (StringUtils.IsNull(addressID))
        {
            throw new ValidationException("Address ID must be entered");
        }

        AddressDAO addressDAO = new AddressDAO();
        return addressDAO.GetAddress(addressID);
    }
}

AddressBO और AddressDAO के बीच एक स्पष्ट सीम है। चलिए एड्रेसडीएओ के लिए एक इंटरफ़ेस बनाते हैं और निर्भरता को एड्रेसबो में इंजेक्ट करने की अनुमति देते हैं।

public interface IAddressDAO
{
  TransferObject GetAddress(addressID);
  //add other interface methods here.
}

public class AddressDAO:GenericDAO, IAddressDAO
{
  public TransferObject GetAddress(string addressID)
  {
    ///implementation goes here
  }
}

अब आप इंजेक्शन के लिए अनुमति देने के लिए अपने AddressBO को डॉक्टर

public class AddressBO
{
    private IAddressDAO _addressDAO;
    public AddressBO()
    {
      _addressDAO = new AddressDAO();
    }

    public AddressBO(IAddressDAO addressDAO)
    {
      _addressDAO = addressDAO;
    }

    public TransferObject GetAddress(string addressID)
    {
        if (StringUtils.IsNull(addressID))
        {
            throw new ValidationException("Address ID must be entered");
        }
        //call the injected AddressDAO
        return _addressDAO.GetAddress(addressID);
    }
}

यहां हम "गरीब आदमी की निर्भरता इंजेक्शन" का उपयोग कर रहे हैं। हमारा एकमात्र लक्ष्य सीम को तोड़ना और हमें एड्रेसबो का परीक्षण करने की अनुमति देना है। अब हमारे यूनिट परीक्षणों में हम एक IAddressDAO का मज़ाक बना सकते हैं और दो वस्तुओं के बीच की बातचीत को मान्य कर सकते हैं।


1
मैं सहमत हूं - जब मैंने इसके बारे में पढ़ा तो मुझे यह रणनीति पसंद आई। हम वास्तव में मूल्य जोड़ने के बिना कोड को साफ करने में महीनों लगा सकते हैं। यदि हम सफाई पर ध्यान केंद्रित करते हैं, तो एक नई विशेषता को जोड़ते हुए हमें जो बदलने की आवश्यकता है वह हमें दोनों दुनियाओं में सबसे अच्छी मिलती है।
एंथनी

एकमात्र चुनौती मौजूदा कोड के लिए यूनिट परीक्षण लिख रही है। मैं पहले उच्च-स्तरीय परीक्षणों की ओर अधिक झुकाव कर रहा हूं ताकि हम अधिक आत्मविश्वास के साथ इकाई परीक्षण को रिफ्लेक्टर और जोड़ सकें।
एंथनी

1
हाँ सबसे अच्छा आप कर सकते हैं परीक्षण लिखता है जो कोड को सत्यापित करता है कि वह क्या करता है। आप परीक्षण बनाने की कोशिश कर सकते हैं जो सही व्यवहार को सत्यापित करते हैं ... लेकिन आप अन्य कार्यक्षमता को तोड़ने का जोखिम चलाते हैं जो परीक्षणों द्वारा कवर नहीं किया गया है।
माइकल ब्राउन

यह सबसे अच्छा स्पष्टीकरण है जिसे मैंने पंख लगाने के लिए देखा है "व्यवहार में एक सीम ढूंढें"। जैसा कि किसी को OO की तुलना में अधिक प्रक्रियात्मक है, पताबो और पताडीएओ के बीच एक स्पष्ट सीम है जो मेरे लिए स्पष्ट नहीं था, लेकिन यह उदाहरण वास्तव में मदद करता है।
सेरा

5

अगर मुझे याद है कि सही तरीके से लिगेसी कोड के साथ काम करना कि फिर से लिखना एक पूर्ण गारंटी देता है कि नया कोड पुराने (कार्यक्षमता / दृष्टिकोण से) पुराने की तुलना में कोई भी बेहतर होगा। बग्स को ठीक करते समय / नई सुविधाओं को जोड़ने के लिए उस पुस्तक में रिफलेक्टरिंग होते हैं।

एक अन्य पुस्तक जो मैं सुझाऊंगा, वह है ब्राउनफील्ड एप्लीकेशन डेवलपमेंट इन .NET जो मूल रूप से एक पूर्ण रूप से फिर से लिखने पर भी नहीं करने के लिए कहता है। जब भी आप नई सुविधाओं को जोड़ते हैं या दोषों को ठीक करते हैं, तो यह स्थिर, पुनरावृति परिवर्तन करने की बात करता है। यह लागत बनाम लाभ के विचारों को संबोधित करता है और एक समय में बहुत अधिक बंद करने की कोशिश करने के बारे में चेतावनी देता है। लिगेसी कोड के साथ प्रभावी रूप से कार्य करते हुए, ज्यादातर माइक्रो / कोड स्तर पर रिफ्लेक्टर करने के बारे में बात करते हैं । .NET में ब्राउनफील्ड एप्लिकेशन डेवलपमेंट , री-फैक्टरिंग (कुछ कोड-स्तर के सामान के साथ) के साथ उच्च स्तर के विचारों को शामिल करता है।

ब्राउनफील्ड पुस्तक यह भी बताती है कि कोड का कौन सा क्षेत्र आपको सबसे अधिक परेशान कर रहा है और वहाँ ध्यान केंद्रित करता है। किसी भी अन्य क्षेत्रों में जिन्हें बहुत अधिक रखरखाव की आवश्यकता नहीं है, वे बदलने के लायक नहीं हो सकते हैं।


किताब के लिए +1 ब्राउनफील्ड एप्लीकेशन डेवलपमेंट इन .Net
गेब्रियल मेगनॉन

पुस्तक सिफारिश के लिए धन्यवाद - मैं इस पर एक नज़र डालूंगा। अवलोकन से, यह विशेष रूप से मेरे द्वारा उल्लिखित दो पुस्तकों की तुलना में .NET पर अधिक फोकस्ड होगा, जो सी, सी ++ और जावा पर ध्यान केंद्रित करते हैं।
एंथनी

4

ऐसी विरासत ऐप के लिए, यूनिट परीक्षणों के बजाय इसे (स्वचालित) उच्च स्तरीय एकीकरण परीक्षणों के साथ कवर करके शुरू करना बहुत अधिक लागत प्रभावी है। फिर अपने सुरक्षा जाल के रूप में एकीकरण परीक्षणों के साथ, आप छोटे कदमों में रिफैक्टिंग शुरू कर सकते हैं यदि यह उचित है, यानी यदि रिफैक्टिंग की लागत लंबे समय में खुद को वापस भुगतान करती है। जैसा कि अन्य ने उल्लेख किया है, यह स्वयं स्पष्ट नहीं है।

इसी तरह के प्रश्न के लिए मेरा पहले वाला उत्तर भी देखें ; उम्मीद है यह आपको उपयोगी होगा।


मैं शुरू करने के लिए उच्च स्तरीय परीक्षणों की ओर झुक रहा हूं। मैं डीबीए के साथ काम कर रहा हूं ताकि प्रत्येक परीक्षण के निष्पादन के बाद डेटाबेस को वापस लाने का सबसे अच्छा तरीका हो।
एंथनी

1

दूर फेंकने और रनिंग कोड को फिर से लिखने के साथ बहुत सावधान रहें ( चीजें जो आपको कभी नहीं करनी चाहिए )। यकीन है कि यह बदसूरत हो सकता है, लेकिन अगर यह काम करता है तो इसे छोड़ दें। जोएल का ब्लॉग पोस्ट देखें, निश्चित रूप से 10+ साल पुराना है, लेकिन लक्ष्य पर अभी भी सही है।


मैं वहां जोएल से असहमत हूं। उन्होंने जो कहा वह उस समय प्रासंगिक लगा होगा, लेकिन क्या यह दोबारा नहीं लिखा गया जिसे अब मोज़िला फ़ायरफ़ॉक्स कहा जाता है?
कैशबैक

1
हां, लेकिन इसने नेटस्केप को व्यवसाय से बाहर कर दिया! यह नहीं कह रहा है कि शुरू करना कभी सही विकल्प नहीं है, लेकिन इसके बारे में बहुत सावधान रहना चाहिए। और OO कोड हमेशा बेहतर नहीं होता है तो प्रक्रियात्मक कोड।
Zachary K

1

जैसा कि माइक ने कहा था कि 'बॉय स्काउट नियम' शायद यहां सबसे अच्छा है, अगर कोड काम करता है और बग रिपोर्ट का एक निरंतर स्रोत नहीं है, तो मैं इसे वहां बैठने देना पसंद करूंगा और समय के साथ धीरे-धीरे इसमें सुधार करूंगा।

अपनी वृद्धि परियोजना के दौरान चीजों को करने के नए तरीकों के लिए अनुमति दें। उदाहरण के लिए नई सुविधाओं के लिए ORM का उपयोग करें और मौजूदा डेटा लेयर पैटर्न को बायपास करें। जब आप एन्हांसमेंट में भाग लेते हैं, जिसे मौजूदा कोड को छूने की आवश्यकता होती है, तो आप कुछ चीजों से संबंधित कोड को नए तरीके से स्थानांतरित करने में सक्षम हो सकते हैं। स्थानों पर एक मुखौटा या कुछ एडेप्टर का उपयोग करने से आपको पुराने कोड को अलग करने में मदद मिल सकती है, शायद प्रति परत भी। यह आपको पुराने कोड को समय के साथ भूखा रखने में मदद कर सकता है।

इसी तरह यह आपको इकाई परीक्षणों को जोड़ने में मदद कर सकता है, आप अपने द्वारा बनाए गए नए कोड के साथ शुरू कर सकते हैं और धीरे-धीरे पुराने कोड के लिए कुछ परीक्षण जोड़ सकते हैं जिसे आपको नए संवर्द्धन के लिए स्पर्श करना है।


1

वे दोनों अच्छी पुस्तकें हैं। यदि आपका उस तरीके से कोड लिखना शुरू करने जा रहा है, तो मुझे लगता है कि इसे फिर से लिखने के साथ ही इसे स्थिर रखने में मदद करने के लिए यूनिट परीक्षणों के साथ कोड को कवर करना भी महत्वपूर्ण है।

इसे छोटे चरणों में किया जाना चाहिए और इस तरह के कोड को संशोधित करने से आसानी से पूरे सिस्टम को अस्थिर किया जा सकता है।

मैं आपके द्वारा सक्रिय रूप से काम नहीं कर रहे किसी भी कोड को संशोधित नहीं करूंगा। केवल इस कोड पर आप सक्रिय रूप से बढ़ा या ठीक कर रहे हैं। अगर कुछ काम कर रहा है, तो यह उद्देश्य है लेकिन वर्षों में इसे संशोधित नहीं किया गया है तो इसे छोड़ दें। अगर आप बेहतर तरीके से जानते हैं तो भी यह काम कर रहा है।

दिन के अंत में कंपनी को उत्पादकता की आवश्यकता होती है। हालांकि बेहतर कोड उत्पादकता पुनर्लेखन कोड को बढ़ाता है, क्योंकि यह बेहतर लिखा जा सकता है, शायद यह आपके उत्पाद का मूल्य लाने का सबसे अच्छा तरीका नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.