Rumah  >  Artikel  >  Tutorial sistem  >  Bagaimana untuk menangani kod yang ditinggalkan oleh pengaturcara sebelumnya

Bagaimana untuk menangani kod yang ditinggalkan oleh pengaturcara sebelumnya

王林
王林ke hadapan
2024-01-19 10:36:161060semak imbas

Walaupun ini boleh menjadi tugas yang membosankan dan menakutkan, disebabkan fleksibiliti menggunakan kod yang ditulis oleh pembangun lain, kami boleh memperoleh manfaat yang ketara daripadanya, termasuk meningkatkan jangkauan kami, membetulkan reput perisian dan mempelajari Bahagian sistem yang kami lakukan' t faham sebelum ini (apatah lagi, mempelajari teknik dan teknik pengaturcara lain).

Memandangkan penggunaan kod yang ditulis oleh pembangun lain mempunyai gangguan serta kelebihannya, kita harus berhati-hati agar tidak membuat beberapa kesilapan yang serius:

  • Kesedaran Diri Kita: Kita mungkin berasa seperti kita tahu yang terbaik, tetapi selalunya perkara itu tidak berlaku. Apa yang kami ubah ialah kod yang kami tahu serba sedikit - kami tidak tahu niat pengarang asal, keputusan yang membawa kepada kod ini, alat dan rangka kerja yang tersedia kepada pengarang asal semasa mereka menulis kod, dsb. Kualiti merendah diri bernilai ribuan ringgit, anda layak mendapatnya.
  • Kesedaran diri pengarang asal: Kod yang akan kami sentuh telah ditulis oleh pembangun lain, dengan gaya, kekangan, tarikh akhir dan kehidupan peribadi yang lain (mengambil masa di luar kerja). Hanya apabila kita mula mempersoalkan keputusan yang dia buat atau mempersoalkan mengapa kod itu sangat najis, orang itu merenung dirinya sendiri dan berhenti menjadi sombong. Kita harus berusaha sedaya upaya agar pengarang asal membantu kerja kita, bukan menghalang kita.
  • Takut kepada yang tidak diketahui: Banyak kali, kod yang akan kita hubungi adalah sesuatu yang kita tidak tahu atau tidak tahu apa-apa. Inilah bahagian yang menakutkan: Kami akan bertanggungjawab atas sebarang perubahan yang kami lakukan, tetapi pada asasnya kami berjalan-jalan di dalam bilik gelap tanpa cahaya. Daripada bimbang, kita harus membina struktur yang membolehkan kita berasa selesa dengan perubahan besar dan kecil, dan membolehkan kita memastikan kita tidak merosakkan fungsi sedia ada.

Memandangkan pembangun, termasuk diri kita sendiri, adalah manusia, adalah berguna untuk menangani banyak isu sifat manusia apabila bekerja dengan kod yang ditulis oleh pembangun lain. Dalam artikel ini, kita akan melalui lima teknik yang boleh kita gunakan untuk memastikan pemahaman kita tentang sifat manusia digunakan untuk kelebihan kita, mendapatkan bantuan sebanyak mungkin daripada kod sedia ada dan pengarang asal, dan membolehkan kod yang ditulis oleh pembangun lain Dalam akhirnya, ia menjadi lebih baik dari sebelumnya. Walaupun 5 kaedah yang disenaraikan di sini tidak menyeluruh, menggunakan teknik di bawah akan memastikan bahawa apabila kami akhirnya menukar kod yang ditulis oleh pembangun lain, kami yakin kami akan memastikan ciri sedia ada berfungsi sambil memastikan ciri baharu kami serasi dengan yang sedia ada. Pangkalan kod diselaraskan.

Bagaimana untuk menangani kod yang ditinggalkan oleh pengaturcara sebelumnya

1. Pastikan ujian itu wujud

Satu-satunya keyakinan sebenar adalah untuk memastikan bahawa fungsi sedia ada yang wujud dalam kod yang ditulis oleh pembangun lain benar-benar berfungsi seperti yang diharapkan dan bahawa sebarang perubahan yang kami lakukan padanya tidak akan menjejaskan fungsi tersebut adalah dengan menyokong kod dengan ujian. Apabila kita menemui kod yang ditulis oleh pembangun lain, kod itu boleh berada dalam dua keadaan: (1) tahap ujian tidak mencukupi, atau (2) tahap ujian mencukupi. Dalam kes terdahulu, kami bertanggungjawab untuk membuat ujian, manakala dalam kes kedua, kami boleh menggunakan ujian sedia ada untuk memastikan bahawa sebarang perubahan yang kami buat tidak melanggar kod dan mendapatkan sebanyak mungkin ujian Memahami niat kod tersebut.

Buat ujian baharu

Ini adalah contoh yang menyedihkan: apabila kami menukar kod pembangun lain, kami bertanggungjawab ke atas keputusan perubahan itu, tetapi tidak ada cara untuk menjamin bahawa kami tidak melanggar apa-apa apabila kami membuat perubahan. Tiada guna mengeluh. Tidak kira dalam apa jua keadaan yang kita temui kod itu, kita perlu menyentuh kod itu, jadi jika kod itu rosak, ia adalah tanggungjawab kita. Jadi apabila kita menukar kod, kita mesti mengawal tingkah laku kita sendiri. Satu-satunya cara untuk memastikan anda tidak akan melanggar kod adalah dengan menulis sendiri ujian.

Walaupun ini membosankan, ia membolehkan kita belajar dengan menulis ujian, yang merupakan kelebihan utamanya. Anggapkan bahawa kod kini berfungsi dengan betul, dan kita perlu menulis ujian supaya input yang dijangkakan membawa kepada output yang dijangkakan. Semasa kami menjalankan ujian ini, kami mempelajari secara beransur-ansur tentang niat dan kefungsian kod tersebut. Contohnya, diberi kod berikut

public class Person {
    private int age;
    private double salary;
    public Person(int age, double salary) {
        this.age = age;
        this.salary = salary;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public double getSalary() {
        return salary;
    }
}
public class SuccessfulFilter implements Predicate {
    @Override
    public boolean test(Person person) {
        return person.getAge()  60000);
    }
}

Kami tidak tahu banyak tentang maksud kod dan sebab nombor ajaib digunakan dalam kod, tetapi kami boleh membuat satu set ujian di mana input yang diketahui menghasilkan output yang diketahui. Sebagai contoh, dengan melakukan beberapa matematik mudah dan menyelesaikan masalah gaji ambang yang membentuk kejayaan, kami mendapati bahawa seseorang itu dianggap berjaya (mengikut piawaian spesifikasi ini) jika dia berumur di bawah 30 tahun dan memperoleh kira-kira $68,330 setahun . Walaupun kita tidak tahu apakah nombor ajaib itu, kita tahu bahawa ia mengurangkan nilai gaji awal. Oleh itu, ambang $68,330 ialah gaji pokok sebelum potongan. Dengan menggunakan maklumat ini, kami boleh membuat beberapa ujian mudah seperti:

public class SuccessfulFilterTest {
    private static final double THRESHOLD_NET_SALARY = 68330.0;
    @Test
    public void under30AndNettingThresholdEnsureSuccessful() {
        Person person = new Person(29, THRESHOLD_NET_SALARY);
        Assert.assertTrue(new SuccessfulFilter().test(person));
    }
    @Test
    public void exactly30AndNettingThresholdEnsureUnsuccessful() {
        Person person = new Person(30, THRESHOLD_NET_SALARY);
        Assert.assertFalse(new SuccessfulFilter().test(person));
    }
    @Test
    public void under30AndNettingLessThanThresholdEnsureSuccessful() {
        Person person = new Person(29, THRESHOLD_NET_SALARY - 1);
        Assert.assertFalse(new SuccessfulFilter().test(person));
    }
}

通过这三个测试,我们现在对现有代码的工作方式有了大致的了解:如果一个人不到30岁,且每年赚$ 68,300,那么他被认为是成功人士。虽然我们可以创建更多的测试来确保临界情况(例如空白年龄或工资)功能正常,但是一些简短的测试不仅使我们了解了原始功能,还给出了一套自动化测试,可用于确保在对现有代码进行更改时,我们不会破坏现有功能。

使用现有测试

如果有足够的代码测试组件,那么我们可以从测试中学到很多东西。正如我们创建测试一样,通过阅读测试,我们可以了解代码如何在功能层面上工作。此外,我们还可以知道原作者是如何让代码运行的。即使测试是由原作者以外的人(在我们接触之前)撰写的,也依然能够为我们提供关于其他人对代码的看法。

虽然现有的测试可以提供帮助,但我们仍然需要对此持保留态度。测试是否与代码的开发更改一起与时俱进是很难说的。如果是的话,那么这是一个很好的理解基础;如果不是,那么我们要小心不要被误导。例如,如果初始的工资阈值是每年75,000美元,而后来更改为我们的68,330美元,那么下面这个过时的测试可能会使我们误入歧途:

@Test
public void under30AndNettingThresholdEnsureSuccessful() {
    Person person = new Person(29, 75000.0);
    Assert.assertTrue(new SuccessfulFilter().test(person));
}

这个测试还是会通过的,但没有了预期的作用。通过的原因不是因为它正好是阈值,而是因为它超出了阈值。如果此测试组件包含这样一个测试用例:当薪水低于阈值1美元时,过滤器就返回false,这样第二个测试将会失败,表明阈值是错误的。如果套件没有这样的测试,那么陈旧的数据会很容易误导我们弄错代码的真正意图。当有疑问时,请相信代码:正如我们之前所表述的那样,求解阈值表明测试没有对准实际阈值。

另外,要查看代码和测试用例的存储库日志(即Git日志):如果代码的最后更新日期比测试的最后更新日期更近(对代码进行了重大更改,例如更改阈值),则测试可能已经过时,应谨慎查看。注意,我们不应该完全忽视测试,因为它们也许仍然能为我们提供关于原作者(或最近撰写测试的开发人员)意图的一些文档,但它们可能包含过时或不正确的数据。

2.与编写代码的人交流

在涉及多个人的任何工作中,沟通至关重要。无论是企业,越野旅行还是软件项目,缺乏沟通是损害任务最有效的手段之一。即使我们在创建新代码时进行沟通,但是当我们接触现有的代码时,风险会增加。因为此时我们对现有的代码并不太了解,因此我们所了解的内容可能是被误导的,或只代表了其中的一小部分。为了真正了解现有的代码,我们需要和编写它的人交流。

当开始提出问题时,我们需要确定问题是具体的,并且旨在实现我们理解代码的目标。例如:

  • 这个代码片段最适合放到系统的哪里?
  • 你有什么设计或图表吗?
  • 我应该注意什么陷阱?
  • 这个组件或类是做什么的?
  • 有没有什么你想放到代码里,但当时没有做的?为什么?

始终要保持谦虚的态度,积极寻求原作者真正的答案。几乎每个开发人员都碰到过这样的场景,他或她看着别人的代码,自问自答:“为什么他/她要这样做?为什么他们不这样做?”然后花几个小时来得出本来只要原作者回答就能得到的结论。大多数开发人员都是有才华的程序员,所以即使如果我们遇到一个看似糟糕的决定,也有可能有一个很好的理由(可能没有,但研究别人的代码时最好假设他们这样做是有原因的;如果真的没有,我们可以通过重构来改变)。

沟通在软件开发中起次要副作用。1967年最初由Melvin Conway创立的康威定律规定:

设计系统的任何组织…都将不可避免地产生一种设计,该设计结构反映了组织的通信结构。

Ini bermakna bahawa pasukan yang besar dan berkomunikasi dengan rapat mungkin menghasilkan kod bersepadu dan berganding rapat, tetapi sesetengah pasukan yang lebih kecil mungkin menghasilkan kod yang lebih bebas dan berganding longgar (untuk mendapatkan maklumat lanjut tentang korelasi ini, sila Lihat "Menyahmistikan Undang-undang Conway"). Bagi kami, ini bermakna struktur komunikasi kami bukan sahaja mempengaruhi kepingan kod tertentu, tetapi keseluruhan pangkalan kod. Oleh itu, komunikasi rapat dengan penulis asal sudah pasti cara yang baik, tetapi kita harus menyemak diri kita untuk tidak terlalu bergantung kepada penulis asal. Ini bukan sahaja mungkin mengganggu pengarang asal, ia juga boleh mencipta gandingan yang tidak disengajakan dalam kod kami.

Walaupun ini membantu kami menyelidiki kod dengan lebih mendalam, ini menganggap akses kepada pengarang asal. Dalam banyak kes, pengarang asal mungkin telah meninggalkan syarikat, atau kebetulan berada jauh dari syarikat (seperti sedang bercuti). Apa yang perlu kita lakukan dalam keadaan ini? Tanya sesiapa sahaja yang mungkin mengetahui sesuatu tentang kod tersebut. Orang ini tidak semestinya benar-benar mengerjakan kod itu; dia mungkin ada semasa pengarang asal menulis kod itu, atau dia boleh mengenali pengarang asal. Walaupun hanya beberapa perkataan daripada orang yang rapat dengan pembangun asal boleh mencerahkan coretan kod lain yang tidak diketahui.

3. Alih keluar semua amaran

Terdapat konsep terkenal dalam psikologi yang dipanggil "teori tingkap pecah", yang diterangkan secara terperinci oleh Andrew Hunt dan Dave Thomas dalam "The Pragmatic Programmer" (halaman 4-6). Teori ini pada asalnya dicadangkan oleh James Q. Wilson dan George L. Kelling dan diterangkan seperti berikut:

Andaikan ada sebuah bangunan dengan beberapa tingkap pecah. Jika tingkap tidak dibaiki, maka vandal akan cenderung untuk memecahkan lebih banyak tingkap. Akhirnya, mereka mungkin menceroboh masuk, menduduki bangunan secara haram, atau menyalakan api di dalam jika bangunan itu tidak berpenghuni. Pertimbangkan juga keadaan kaki lima. Jika sampah terkumpul di jalan raya, lebih banyak sampah akan terkumpul tidak lama lagi. Akhirnya, orang ramai akan mula membuang sampah bawa pulang ke sana dan juga memecahkan kereta.

Teori ini menyatakan sekiranya tiada sesiapa yang kelihatan mengambil berat tentang barang atau benda tersebut, maka kita akan mengabaikan penjagaan barang atau benda tersebut iaitu fitrah manusia. Sebagai contoh, jika bangunan sudah kelihatan tidak kemas, ia berkemungkinan besar untuk dirosakkan. Dari segi perisian, teori ini bermakna jika pembangun mendapati bahawa kod tersebut adalah kucar-kacir, ia adalah sifat manusia untuk memecahkannya. Pada asasnya, apa yang kita fikirkan (walaupun aktiviti mental tidak begitu kaya) ialah, "Jika orang terakhir tidak mengambil berat tentang kod ini, mengapa saya perlu mengambil berat atau "Ia hanya kod yang kacau, siapa tahu siapa yang menulis?" itu."

Namun, ini tidak sepatutnya menjadi alasan kita. Setiap kali kami menyentuh kod yang sebelum ini milik orang lain, kami bertanggungjawab ke atas kod tersebut dan menanggung akibatnya jika ia tidak berfungsi dengan berkesan. Untuk mengalahkan tingkah laku semulajadi manusia ini, kita perlu mengambil langkah kecil untuk mengelakkan kod kita menjadi lebih jarang kotor (gantikan tingkap yang pecah dengan segera).

Cara mudah ialah dengan mengalih keluar semua amaran daripada keseluruhan pakej atau modul yang kami gunakan. Bagi kod yang tidak digunakan atau diulas, padamkannya. Jika kita memerlukan bahagian kod ini kemudian, dalam repositori kita sentiasa boleh mendapatkannya daripada komit sebelumnya. Jika terdapat amaran yang tidak boleh diselesaikan secara langsung (seperti amaran jenis primitif), maka anotasi panggilan atau kaedah dengan anotasi @SuppressWarnings. Ini memastikan bahawa kami telah memikirkan dengan teliti tentang kod kami: ia bukan amaran kerana ketidaksengajaan, tetapi amaran yang kami perhatikan secara eksplisit (seperti jenis primitif).

Setelah kami mengalih keluar atau menyekat semua amaran secara eksplisit, maka kami mesti memastikan kod tersebut kekal bebas daripada amaran. Ini mempunyai dua kesan utama:

  • Memaksa kami untuk berfikir dengan teliti tentang sebarang kod yang kami cipta.
  • Kurangkan perubahan rasuah kod di mana amaran kini membawa kepada ralat kemudian.

Ini mempunyai implikasi psikologi untuk orang lain, serta diri kita sendiri - bahawa kita sebenarnya mengambil berat tentang kod yang sedang kita usahakan. Ia bukan lagi jalan sehala - kami memaksa diri kami untuk menukar kod, melaksanakannya dan tidak pernah melihat ke belakang. Sebaliknya, kami menyedari bahawa kami perlu bertanggungjawab ke atas kod tersebut. Ini juga berguna untuk pembangunan perisian masa depan - ia menunjukkan kepada pembangun masa depan bahawa ini bukan gudang dengan tingkap yang rosak: ia adalah pangkalan kod yang diselenggara dengan baik.

4.Refactor

Pemfaktoran semula telah menjadi istilah yang sangat penting sejak beberapa dekad yang lalu dan baru-baru ini telah digunakan sebagai sinonim untuk membuat sebarang perubahan pada kod yang sedang berfungsi. Walaupun pemfaktoran semula melibatkan perubahan pada kod yang sedang anda kerjakan, ia bukan gambaran keseluruhannya. Martin Fowler, dalam buku seminalnya mengenai topik, Refactoring, mentakrifkan refactoring sebagai:

Buat perubahan pada struktur dalaman perisian untuk menjadikannya lebih mudah difahami dan lebih murah untuk diubah suai tanpa mengubah tingkah laku yang boleh diperhatikan.

这个定义的关键在于它涉及的更改不会改变系统可观察的行为。这意味着当我们重构代码时,我们必须要有方法来确保代码的外部可见行为不会改变。在我们的例子中,这意味着是在我们继承或自己开发的测试套件中。为了确保我们没有改变系统的外部行为,每当我们进行改变时,都必须重新编译和执行我们的全部测试。

此外,并不是我们所做的每一个改变都被认为是重构。例如,重命名方法以更好地反映其预期用途是重构,但添加新功能不是。为了看到重构的好处,我们将重构SuccessfulFilter。执行的第一个重构是提取方法,以更好地封装个人净工资的逻辑:

public class SuccessfulFilter implements Predicate {
    @Override
    public boolean test(Person person) {
        return person.getAge()  60000;
    }
    private double getNetSalary(Person person) {
        return (((person.getSalary() - (250 * 12)) - 1500) * 0.94);
    }
}

在我们进行这种改变之后,我们重新编译并运行我们的测试套件,测试套件将继续通过。现在更容易看出,成功是通过一个人的年龄和净薪酬定义的,但是getNetSalary方法似乎并不像Person类一样属于SuccessfulFilter(指示标志就是该方法的唯一参数是Person,该方法的唯一调用是Person类的方法,因此对Person类有很强的亲和力)。 为了更好地定位这个方法,我们执行一个Move方法将其移动到Person类:

public class Person {
    private int age;
    private double salary;
    public Person(int age, double salary) {
        this.age = age;
        this.salary = salary;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public double getSalary() {
        return salary;
    }
    public double getNetSalary() {
        return ((getSalary() - (250 * 12)) - 1500) * 0.94;
    }
}
public class SuccessfulFilter implements Predicate {
    @Override
    public boolean test(Person person) {
        return person.getAge()  60000;
    }
}

为了进一步清理此代码,我们对每个magic number执行符号常量替换magic number行为。为了知道这些值的含义,我们可能得和原作者交流,或者向具有足够领域知识的人请教,以引领正确的方向。我们还将执行更多的提取方法重构,以确保现有的方法尽可能简单。

public class Person {
    private static final int MONTHLY_BONUS = 250;
    private static final int YEARLY_BONUS = MONTHLY_BONUS * 12;
    private static final int YEARLY_BENEFITS_DEDUCTIONS = 1500;
    private static final double YEARLY_401K_CONTRIBUTION_PERCENT = 0.06;
    private static final double YEARLY_401K_CONTRIBUTION_MUTLIPLIER = 1 - YEARLY_401K_CONTRIBUTION_PERCENT;
    private int age;
    private double salary;
    public Person(int age, double salary) {
        this.age = age;
        this.salary = salary;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public double getSalary() {
        return salary;
    }
    public double getNetSalary() {
        return getPostDeductionSalary();
    }
    private double getPostDeductionSalary() {
        return getPostBenefitsSalary() * YEARLY_401K_CONTRIBUTION_MUTLIPLIER;
    }
    private double getPostBenefitsSalary() {
        return getSalary() - YEARLY_BONUS - YEARLY_BENEFITS_DEDUCTIONS;
    }
}
public class SuccessfulFilter implements Predicate {
    private static final int THRESHOLD_AGE = 30;
    private static final double THRESHOLD_SALARY = 60000.0;
    @Override
    public boolean test(Person person) {
        return person.getAge()  THRESHOLD_SALARY;
    }
}

重新编译和测试,发现系统仍然按照预期的方式工作:我们没有改变外部行为,但是我们改进了代码的可靠性和内部结构。有关更复杂的重构和重构过程,请参阅Martin Fowler的Refactoring Guru网站。

5.当你离开的时候,代码比你发现它的时候更好

最后这个技术在概念上非常简单,但在实践中很困难:让代码比你发现它的时候更好。当我们梳理代码,特别是别人的代码时,我们大多会添加功能,测试它,然后前行,不关心我们会不会贡献软件腐烂,也不在乎我们添加到类的新方法会不会导致额外的混乱。因此,本文的全部内容可总结为以下规则:

每当我们修改代码时,请确保当你离开的时候,代码比你发现它的时候更好。

前面提到过,我们需要对类造成的损坏和对改变的代码负责,如果它不能工作,那么修复是我们的职责。为了战胜伴随软件生产而出现的熵,我们必须强制自己做到离开时的代码比我们发现它的时候更佳。为了不逃避这个问题,我们必须偿还技术债务,确保下一个接触代码的人不需要再付出代价。说不定,将来可能是我们自己感谢自己这个时候的坚持呢。

Atas ialah kandungan terperinci Bagaimana untuk menangani kod yang ditinggalkan oleh pengaturcara sebelumnya. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:linuxprobe.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam