Open puddlejumper26 opened 4 years ago
currentFont.sizeInPixels = PointsToPixels( 12 )
currentFont.SetSizeInPoints( sizeInPoints )
currentFont.SetSizeInPixels( sizeInPixels )
currentFont.SetBoldOn()
currentFont.SetBoldOff()
currentFont.SetItalicOn()
currentFont.SetItalicOff()
currentFont.SetTypeFace( faceName )
Inheritance is the idea that one class is a specialization of another class.
to create simpler code by defining a base class that specifies common elements of two or more derived classes
Implement “is a” through public inheritance
Design and document for inheritance or prohibit it
Adhere to the Liskov Substitution Principle (LSP) ( you shouldn’t inherit from a base class unless the derived class truly “is a” more specific version of the base class)
Be sure to inherit only what you want to inherit
inherited routines come in three basic flavors: ■ An abstract overridable routine means that the derived class inherits the routine’s interface but not its implementation. ■ An overridable routine means that the derived class inherits the routine’s interface and a default implementation and it is allowed to override the default implementation. ■ A non-overridable routine means that the derived class inherits the routine’s interface and its default implementation and it is not allowed to override the routine’s implementation.
Don’t “override” a non-overridable member function (Don’t reuse names of non-overridable base-class routines in derived classes)
Move common interfaces, data, and behavior as high as possible in the inheritance tree (The higher you move interfaces, data, and behavior, the more easily derived classes can use them)
Be suspicious of classes of which there is only one instance (The Singleton pattern is one notable exception to this guideline)
Be suspicious of base classes of which there is only one derived class
Be suspicious of classes that override a routine and do nothing inside the derived routine
Avoid deep inheritance trees (Deep inheritance trees increase complexity)
Prefer polymorphism to extensive type checking (Frequently repeated case statements)
Make all data private, not protected
-
Multiple Inheritance
multiple inheritance is useful primarily for defining “mixins,”
■ If multiple classes share common data but not behavior, create a common object that those classes can contain. ■ If multiple classes share common behavior but not data, derive them from a common base class that defines the common routines. ■ If multiple classes share common data and behavior, inherit from a common base class that defines the common data and routines. ■ Inherit when you want the base class to control your interface; contain when you want to control your interface
■ Naming conventions that differentiate which classes are public and which are for the package’s private use ■ Naming conventions, code-organization conventions (project structure), or both that identify which package each class belongs to ■ Rules that define which packages are allowed to use which other packages, including whether the usage can be inheritance, containment, or both
❑ Have you thought of the classes in your program as abstract data types and evaluated their interfaces from that point of view?
❑ Does the class have a central purpose? ❑ Is the class well named, and does its name describe its central purpose? ❑ Does the class’s interface present a consistent abstraction? ❑ Does the class’s interface make obvious how you should use the class? ❑ Is the class’s interface abstract enough that you don’t have to think about how its services are implemented? Can you treat the class as a black box? ❑ Are the class’s services complete enough that other classes don’t have to meddle with its internal data? ❑ Has unrelated information been moved out of the class? ❑ Have you thought about subdividing the class into component classes, and have you subdivided it as much as you can? ❑ Are you preserving the integrity of the class’s interface as you modify the class?
❑ Does the class minimize accessibility to its members? ❑ Does the class avoid exposing member data? ❑ Does the class hide its implementation details from other classes as much as the programming language permits? ❑ Does the class avoid making assumptions about its users, including its derived classes? ❑ Is the class independent of other classes? Is it loosely coupled?
❑ Is inheritance used only to model “is a” relationships—that is, do derived classes adhere to the Liskov Substitution Principle? ❑ Does the class documentation describe the inheritance strategy? ❑ Do derived classes avoid “overriding” non-overridable routines? ❑ Are common interfaces, data, and behavior as high as possible in the inheritance tree? ❑ Are inheritance trees fairly shallow? ❑ Are all data members in the base class private rather than protected?
❑ Does the class contain about seven data members or fewer? ❑ Does the class minimize direct and indirect routine calls to other classes? ❑ Does the class collaborate with other classes only to the extent absolutely necessary? ❑ Is all member data initialized in the constructor? ❑ Is the class designed to be used as deep copies rather than shallow copies unless there’s a measured reason to create shallow copies?
❑ Have you investigated the language-specific issues for classes in your specific programming language?
--
--
■ Isolate complexity ■ Hide implementation details ■ Limit effects of changes ■ Hide global data ■ Make central points of control ■ Facilitate reusable code ■ Accomplish a specific refactoring
--
■ const for declaring constant values ■ inline for defining functions that will be compiled as inline code ■ template for defining standard operations like min, max, and so on in a type-safe way ■ enum for defining enumerated types ■ typedef for defining simple type substitutions
❑ Is the reason for creating the routine sufficient? ❑ Have all parts of the routine that would benefit from being put into routines of their own been put into routines of their own? ❑ Is the routine’s name a strong, clear verb-plus-object name for a procedure or a description of the return value for a function? ❑ Does the routine’s name describe everything the routine does? ❑ Have you established naming conventions for common operations? ❑ Does the routine have strong, functional cohesion—doing one and only one thing and doing it well? ❑ Do the routines have loose coupling—are the routine’s connections to other routines small, intimate, visible, and flexible? ❑ Is the length of the routine determined naturally by its function and logic, rather than by an artificial coding standard?
❑ Does the routine’s parameter list, taken as a whole, present a consistent interface abstraction? ❑ Are the routine’s parameters in a sensible order, including matching the order of parameters in similar routines? ❑ Are interface assumptions documented? ❑ Does the routine have seven or fewer parameters? ❑ Is each input parameter used? ❑ Is each output parameter used? ❑ Does the routine avoid using input parameters as working variables? ❑ If the routine is a function, does it return a valid value under all possible circumstances?
■ The most important reason for creating a routine is to improve the intellectual manageability of a program, and you can create a routine for many other good reasons. Saving space is a minor reason; improved readability, reliability, and modifiability are better reasons. ■ Sometimes the operation that most benefits from being put into a routine of its own is a simple one. ■ You can classify routines into various kinds of cohesion, but you can make most routines functionally cohesive, which is best. ■ The name of a routine is an indication of its quality. If the name is bad and it’s accurate, the routine might be poorly designed. If the name is bad and it’s inaccurate, it’s not telling you what the program does. Either way, a bad name means that the program needs to be changed. ■ Functions should be used only when the primary purpose of the function is to return the specific value described by the function’s name. ■ Careful programmers use macro routines with care and only as a last resort.
--
❑ Does the routine protect itself from bad input data? ❑ Have you used assertions to document assumptions, including preconditions and postconditions? ❑ Have assertions been used only to document conditions that should never occur? ❑ Does the architecture or high-level design specify a specific set of errorhandling techniques? ❑ Does the architecture or high-level design specify whether error handling should favor robustness or correctness? ❑ Have barricades been created to contain the damaging effect of errors and reduce the amount of code that has to be concerned about error processing? ❑ Have debugging aids been used in the code? ❑ Have debugging aids been installed in such a way that they can be activated or deactivated without a great deal of fuss? ❑ Is the amount of defensive programming code appropriate—neither too much nor too little? ❑ Have you used offensive-programming techniques to make errors difficult to overlook during development?
❑ Has your project defined a standardized approach to exception handling? ❑ Have you considered alternatives to using an exception? ❑ Is the error handled locally rather than throwing a nonlocal exception, if possible? ❑ Does the code avoid throwing exceptions in constructors and destructors? ❑ Are all exceptions at the appropriate levels of abstraction for the routines that throw them? ❑ Does each exception include all relevant exception background information? ❑ Is the code free of empty catch blocks? (Or if an empty catch block truly is appropriate, is it documented?)
❑ Does the code that checks for bad input data check for attempted buffer overflows, SQL injection, HTML injection, integer overflows, and other malicious inputs? ❑ Are all error-return codes checked? ❑ Are all exceptions caught? ❑ Do error messages avoid providing information that would help an attacker break into the systems?
--
■ Check the routine’s interface. ■ Check for general design quality. ■ Check the routine’s variables. ■ Check the routine’s statements and logic. ■ Check the routine’s layout. ■ Check the routine’s documentation. ■ Remove redundant comments.
--
■ Unit testing is the execution of a complete class, routine, or small program that has been written by a single programmer or team of programmers, which is tested in isolation from the more complete system.
■ Component testing is the execution of a class, package, small program, or other program element that involves the work of multiple programmers or programming teams, which is tested in isolation from the more complete system.
■ Integration testing is the combined execution of two or more classes, packages, components, or subsystems that have been created by multiple programmers or programming teams.
■ Regression testing is the repetition of previously executed test cases for the purpose of finding defects in software that previously passed the same set of tests.
■ System testing is the execution of the software in its final configuration, including integration with other software and hardware systems. It tests for security, performance, resource loss, timing problems, and other issues that can’t be tested at lower levels of integration.
“testing” refers to testing by the developer, which typically consists of unit tests, component tests, and integration tests but can sometimes include regression tests and system tests.
Testing is usually broken into two broad categories: black-box testing and white-box (or glass-box) testing. -- Black box testing - refers to tests in which the tester cannot see the inner workings of the item being tested -- White box testing - refers to tests in which the tester is aware of the inner workings of the item being tested.
Testing is a means of detecting errors.
Debugging is a means of diagnosing and correcting the root causes of errors that have already been detected
■ Testing’s goal runs counter to the goals of other development activities. ■ Testing can never completely prove the absence of errors. ■ Testing by itself does not improve software quality. ■ Testing requires you to assume that you’ll find errors in your code.
■ Test for each relevant requirement to make sure that the requirements have been implemented. ■ Test for each relevant design concern to make sure that the design has been implemented. ■ Use “basis testing” to add detailed test cases to those that test the requirements and the design. ■ Use a checklist of the kinds of errors you’ve made on the project to date or have made on previous projects.
Test First
■ doesn’t take any more effort than writing test cases after the code ■ detect defects earlier and can correct them more easily ■ think at least a little bit about the requirements and design before writing code ■ exposes requirements problems sooner ■
■ Too little data (or no data) ■ Too much data ■ The wrong kind of data (invalid data) ■ The wrong size of data ■ Uninitialized data
■ Nominal cases—middle-of-the-road, expected values ■ Minimum normal configuration ■ Maximum normal configuration ■ Compatibility with old data
■ Administrative description of the defect (the date reported, the person who reported it, a title or description, the build number, the date fixed) ■ Full description of the problem ■ Steps to take to repeat the problem ■ Suggested workaround for the problem ■ Related defects ■ Severity of the problem—for example, fatal, bothersome, or cosmetic ■ Origin of the defect: requirements, design, coding, or testing ■ Subclassification of a coding defect: off-by-one, bad assignment, bad array index, bad routine call, and so on ■ Classes and routines changed by the fix ■ Number of lines of code affected by the defect ■ Hours to find the defect ■ Hours to fix the defect Number of defects in each class, sorted from worst class to best, possibly normalized by class size ■ Number of defects in each routine, sorted from worst routine to best, possibly normalized by routine size ■ Average number of testing hours per defect found ■ Average number of defects found per test case ■ Average number of programming hours per defect fixed ■ Percentage of code covered by test cases ■ Number of outstanding defects in each severity classification
--
--
--
Normally, this book requires a lot of practical experiences. However,
Reference
[1] https://github.com/xianshenglu/document/blob/master/Code%20Complete%202nd%20Edition.pdf
Guideline
Chapter 1 || Chapter 2-Metaphors for a Richer Understanding of Software Development || Chapter 3-Measure Twice, Cut Once: Upstream Prerequisites || Chapter 4-Key Construction Decisions || Chapter 5-Design in Construction || Chapter 6-Working Classes || Chapter 7-High-Quality Routines || Chapter 8-Defensive Programming || Chapter 9-The Pseudocode Programming Process || Chapter 10-General Issues in Using Variables || ..... || Chapter 22-Developer Testing || Chapter 23-Debugging || Chapter 24-Refactoring || ..... ||