Heim >System-Tutorial >LINUX >Wie man mit Code umgeht, den ein früherer Programmierer hinterlassen hat
Obwohl dies eine mühsame und entmutigende Aufgabe sein kann, können wir aufgrund der Flexibilität, von anderen Entwicklern geschriebenen Code zu verwenden, erhebliche Vorteile daraus ziehen, einschließlich der Vergrößerung unserer Reichweite, der Beseitigung von Softwarefäule und dem Erlernen von Teilen des Systems, die wir nicht kennengelernt haben. Ich habe es vorher nicht verstanden (ganz zu schweigen davon, dass ich die Techniken und Techniken anderer Programmierer gelernt habe).
Angesichts der Tatsache, dass die Verwendung von Code, der von anderen Entwicklern geschrieben wurde, Ärger und Vorteile mit sich bringt, müssen wir aufpassen, dass wir keine schwerwiegenden Fehler machen:
Da Entwickler, uns eingeschlossen, Menschen sind, ist es sinnvoll, sich mit vielen Problemen der menschlichen Natur auseinanderzusetzen, wenn man mit Code arbeitet, der von anderen Entwicklern geschrieben wurde. In diesem Artikel gehen wir fünf Techniken durch, mit denen wir sicherstellen können, dass unser Verständnis der menschlichen Natur zu unserem Vorteil genutzt wird, so viel Hilfe wie möglich von vorhandenem Code und Originalautoren erhalten und Code ermöglichen, der von anderen Entwicklern geschrieben wurde Am Ende wird es besser als zuvor. Auch wenn die fünf hier aufgeführten Methoden keinen Anspruch auf Vollständigkeit erheben, stellt die Verwendung der folgenden Techniken sicher, dass wir, wenn wir am Ende den von anderen Entwicklern geschriebenen Code ändern, zuversichtlich sind, dass wir die vorhandenen Funktionen funktionsfähig halten und gleichzeitig sicherstellen, dass unsere neuen Funktionen mit den vorhandenen konsistent sind. Die Codebasis ist harmonisiert.
1. Stellen Sie sicher, dass der Test vorhanden istDie einzige wirkliche Sicherheit besteht darin, sicherzustellen, dass die vorhandene Funktionalität, die in von anderen Entwicklern geschriebenem Code vorhanden ist, tatsächlich wie erwartet funktioniert und dass alle Änderungen, die wir daran vornehmen, die Funktionalität nicht beeinträchtigen. Die Möglichkeit, dies zu erreichen, besteht darin, den Code durch Tests zu unterstützen. Wenn wir auf Code stoßen, der von einem anderen Entwickler geschrieben wurde, kann sich der Code in zwei Zuständen befinden: (1) Es ist nicht genügend Teststufe vorhanden, oder (2) Es ist genügend Teststufe vorhanden. Im ersteren Fall sind wir für die Erstellung der Tests verantwortlich, während wir im letzteren Fall vorhandene Tests verwenden können, um sicherzustellen, dass alle von uns vorgenommenen Änderungen den Code nicht beschädigen und so viel wie möglich aus den Tests herausholen, um die Absicht zu verstehen der Code.
Neuen Test erstellenDas ist ein trauriges Beispiel: Wenn wir den Code anderer Entwickler ändern, sind wir für die Ergebnisse der Änderung verantwortlich, aber es gibt keine Garantie dafür, dass wir bei der Änderung nichts kaputt machen. Es hat keinen Sinn, sich zu beschweren. Egal in welchem Zustand wir den Code finden, wir müssen den Code trotzdem anfassen. Wenn der Code also kaputt geht, liegt es in unserer Verantwortung. Wenn wir also den Code ändern, müssen wir unser eigenes Verhalten kontrollieren. Der einzige Weg, um sicherzustellen, dass Sie den Code nicht beschädigen, besteht darin, die Tests selbst zu schreiben.
Obwohl dies mühsam ist, ermöglicht es uns, durch das Schreiben von Tests zu lernen, was der Hauptvorteil ist. Gehen Sie davon aus, dass der Code jetzt ordnungsgemäß funktioniert und wir Tests schreiben müssen, damit erwartete Eingaben zu erwarteten Ausgaben führen. Während wir diesen Test durcharbeiteten, lernten wir nach und nach die Absicht und Funktionalität des Codes kennen. Zum Beispiel mit dem folgenden Code
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); } }
Wir wissen nicht viel über die Absicht des Codes und warum magische Zahlen im Code verwendet werden, aber wir können eine Reihe von Tests erstellen, bei denen bekannte Eingaben bekannte Ausgaben erzeugen. Durch einfache Berechnungen und die Lösung des Schwellengehaltsproblems, das Erfolg ausmacht, stellen wir beispielsweise fest, dass eine Person (nach den Maßstäben dieser Spezifikation) als erfolgreich gilt, wenn sie unter 30 Jahre alt ist und etwa 68.330 US-Dollar pro Jahr verdient. Obwohl wir diese magischen Zahlen nicht kennen, wissen wir, dass sie den anfänglichen Gehaltswert verringern. Daher ist der Schwellenwert von 68.330 US-Dollar das Grundgehalt vor Abzügen. Mithilfe dieser Informationen können wir einige einfache Tests erstellen, wie zum Beispiel:
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创立的康威定律规定:
设计系统的任何组织…都将不可避免地产生一种设计,该设计结构反映了组织的通信结构。
Das bedeutet, dass ein großes, eng kommuniziertes Team möglicherweise integrierten, eng gekoppelten Code produziert, einige kleinere Teams jedoch möglicherweise unabhängigeren, lose gekoppelten Code produzieren (weitere Informationen zu dieser Korrelation finden Sie unter „Conways Gesetz entmystifizieren“). Für uns bedeutet das, dass sich unsere Kommunikationsstruktur nicht nur auf einzelne Codeteile, sondern auf die gesamte Codebasis auswirkt. Daher ist eine enge Kommunikation mit dem ursprünglichen Autor auf jeden Fall ein guter Weg, wir sollten jedoch darauf achten, uns nicht zu sehr auf den ursprünglichen Autor zu verlassen. Dies dürfte nicht nur den ursprünglichen Autor verärgern, sondern möglicherweise auch zu einer unbeabsichtigten Kopplung in unserem Code führen.
Dies hilft uns zwar, tiefer in den Code einzutauchen, setzt jedoch den Zugriff auf den ursprünglichen Autor voraus. In vielen Fällen hat der ursprüngliche Autor das Unternehmen verlassen oder ist gerade nicht im Unternehmen (z. B. im Urlaub). Was sollen wir in dieser Situation tun? Fragen Sie jeden, der etwas über den Code weiß. Diese Person muss nicht unbedingt an dem Code gearbeitet haben; sie könnte dabei gewesen sein, als der ursprüngliche Autor den Code geschrieben hat, oder sie könnte den ursprünglichen Autor kennen. Schon ein paar Worte von Personen, die dem ursprünglichen Entwickler nahe stehen, können andere unbekannte Codeschnipsel aufklären.
3. Alle Warnungen entfernenIn der Psychologie gibt es ein bekanntes Konzept namens „Broken-Windows-Theorie“, das ausführlich von Andrew Hunt und Dave Thomas in „The Pragmatic Programmer“ (Seiten 4-6) beschrieben wird. Diese Theorie wurde ursprünglich von James Q. Wilson und George L. Kelling vorgeschlagen und wird wie folgt beschrieben:
Angenommen, es gibt ein Gebäude mit mehreren kaputten Fenstern. Wenn Fenster nicht repariert werden, neigen Vandalen dazu, mehr Fenster einzuschlagen. Schließlich kann es sogar passieren, dass sie einbrechen, das Gebäude illegal besetzen oder im Inneren ein Feuer entfachen, wenn das Gebäude unbewohnt ist. Berücksichtigen Sie auch den Zustand der Gehwege. Wenn sich auf der Straße Müll ansammelt, wird sich bald noch mehr Müll ansammeln. Irgendwann werden die Leute sogar anfangen, Müll zum Mitnehmen dorthin zu werfen und sogar Autos kaputtzumachen.
Diese Theorie besagt, dass wir die Sorge um den Gegenstand oder die Sache ignorieren, wenn sich niemand um den Gegenstand oder die Sache kümmert. Das liegt in der Natur des Menschen. Wenn ein Gebäude beispielsweise bereits unordentlich aussieht, ist die Gefahr von Vandalismus höher. In Bezug auf Software bedeutet diese Theorie: Wenn ein Entwickler feststellt, dass der Code ein Chaos ist, liegt es in der Natur des Menschen, ihn zu zerstören. Im Wesentlichen denken wir (auch wenn die geistige Aktivität nicht so groß ist): „Wenn sich die letzte Person nicht für diesen Code interessiert, warum sollte es mich dann interessieren?“ oder „Es ist nur ein durcheinandergebrachter Code, wer weiß, wer ihn geschrieben hat.“ es."
Dies sollte jedoch nicht unsere Entschuldigung sein. Wann immer wir Code berühren, der zuvor jemand anderem gehörte, sind wir für diesen Code verantwortlich und tragen die Konsequenzen, wenn er nicht effektiv funktioniert. Um dieses natürliche menschliche Verhalten zu besiegen, müssen wir kleine Schritte unternehmen, um zu verhindern, dass unser Code weniger häufig schmutzig wird (z. B. indem wir kaputte Fenster umgehend ersetzen).
Eine einfache Möglichkeit besteht darin, alle Warnungen aus dem gesamten von uns verwendeten Paket oder Modul zu entfernen. Löschen Sie unbenutzten oder kommentierten Code. Wenn wir diesen Teil des Codes später benötigen, können wir ihn im Repository jederzeit aus einem früheren Commit abrufen. Wenn Warnungen vorliegen, die nicht direkt aufgelöst werden können (z. B. Warnungen zu primitiven Typen), kommentieren Sie den Aufruf oder die Methode mit der Annotation @SuppressWarnings. Dies stellt sicher, dass wir sorgfältig über unseren Code nachgedacht haben: Es handelt sich nicht um Warnungen aufgrund von Unachtsamkeit, sondern um Warnungen, die wir explizit bemerkt haben (wie bei primitiven Typen).
Sobald wir alle Warnungen entfernen oder explizit unterdrücken, müssen wir sicherstellen, dass der Code frei von Warnungen bleibt. Dies hat zwei wesentliche Auswirkungen:
Das hat eine psychologische Implikation für andere Menschen und auch für uns selbst – dass uns der Code, an dem wir arbeiten, tatsächlich am Herzen liegt. Es ist keine Einbahnstraße mehr – wir zwingen uns, den Code zu ändern, ihn zu übernehmen und niemals zurückzublicken. Stattdessen erkennen wir an, dass wir Verantwortung für den Kodex übernehmen müssen. Dies ist auch für die zukünftige Softwareentwicklung hilfreich – es zeigt zukünftigen Entwicklern, dass es sich hier nicht um ein Lagerhaus mit kaputten Fenstern handelt: Es handelt sich um eine gut gepflegte Codebasis.
4.RefactorRefactoring ist in den letzten Jahrzehnten zu einem sehr wichtigen Begriff geworden und wird in letzter Zeit als Synonym für die Durchführung jeglicher Änderungen am aktuell funktionierenden Code verwendet. Beim Refactoring sind zwar Änderungen am Code erforderlich, an dem Sie gerade arbeiten, es handelt sich jedoch nicht um das Gesamtbild. Martin Fowler definiert Refactoring in seinem bahnbrechenden Buch zum Thema Refactoring als:
Nehmen Sie Änderungen an der internen Struktur der Software vor, um sie leichter verständlich und kostengünstiger zu modifizieren, ohne ihr beobachtbares Verhalten zu ändern.
这个定义的关键在于它涉及的更改不会改变系统可观察的行为。这意味着当我们重构代码时,我们必须要有方法来确保代码的外部可见行为不会改变。在我们的例子中,这意味着是在我们继承或自己开发的测试套件中。为了确保我们没有改变系统的外部行为,每当我们进行改变时,都必须重新编译和执行我们的全部测试。
此外,并不是我们所做的每一个改变都被认为是重构。例如,重命名方法以更好地反映其预期用途是重构,但添加新功能不是。为了看到重构的好处,我们将重构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.当你离开的时候,代码比你发现它的时候更好最后这个技术在概念上非常简单,但在实践中很困难:让代码比你发现它的时候更好。当我们梳理代码,特别是别人的代码时,我们大多会添加功能,测试它,然后前行,不关心我们会不会贡献软件腐烂,也不在乎我们添加到类的新方法会不会导致额外的混乱。因此,本文的全部内容可总结为以下规则:
每当我们修改代码时,请确保当你离开的时候,代码比你发现它的时候更好。
前面提到过,我们需要对类造成的损坏和对改变的代码负责,如果它不能工作,那么修复是我们的职责。为了战胜伴随软件生产而出现的熵,我们必须强制自己做到离开时的代码比我们发现它的时候更佳。为了不逃避这个问题,我们必须偿还技术债务,确保下一个接触代码的人不需要再付出代价。说不定,将来可能是我们自己感谢自己这个时候的坚持呢。
Das obige ist der detaillierte Inhalt vonWie man mit Code umgeht, den ein früherer Programmierer hinterlassen hat. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!