code quality etc. idi outline

Upload: vai-nan

Post on 03-Apr-2018

215 views

Category:

Documents


0 download

TRANSCRIPT

  • 7/29/2019 Code Quality Etc. IDI Outline

    1/71

    1

    Code Quality, Maintainability,

    Reusability, Debugging, Testing

    SIF8080, Sep. 27th 2001Customer-driven project

    Carl-Fredrik Srensen mailto:[email protected]

    mailto:[email protected]:[email protected]
  • 7/29/2019 Code Quality Etc. IDI Outline

    2/71

    2

    Spider-web

    ::theoretical

    ::pck_stream

    ::pck_cargo

    ::choke

    ::lift_acco unt::pck_stock

    ::pck_cargo_doc

    ::units

    ::lagkodeserver

    ::pck_check

    ::pck_stream_network

    ::pck_well

    ::sep_test

    ::pck_flowline

    ::pck_facility

    ::pck_system

    ::mathlib

    ::pck_regression

    ::calc_stream::comp_analysis

    ::regularity

    ::well_node

    ::puf

    ::summarise

    ::tbp_product

    ::transfer_basis

    ::value_adjust

  • 7/29/2019 Code Quality Etc. IDI Outline

    3/71

    3

    Outline

    Code Quality & Standards.

    Debugging, logging etc.

    Testing.

  • 7/29/2019 Code Quality Etc. IDI Outline

    4/71

    4

    Key Principles; Coding

    Understandability

    High cohesion

    Loose coupling Code Formatting

    Consistent Naming

    Information hiding Valuable Comments

  • 7/29/2019 Code Quality Etc. IDI Outline

    5/71

    5

    Code Standards (1)

    Why? Gives less defects.

    Easier/cheaper maintenance.

    Several people may work on and understand the samecode.

    Code to be read, not only written.

    Java coding standards:

    The Elements of Java Style; Vermeulen et.al. SIGS Books.

    http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html.

    http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.htmlhttp://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.htmlhttp://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.htmlhttp://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html
  • 7/29/2019 Code Quality Etc. IDI Outline

    6/71

    6

    Code Standards (2)

    General rules:

    SimplicityBuild simple classes and methods.

    Keep it as simple as possible, but not simpler

    (Einstein).

    ClarityEnsure item has a clear purpose. Explain

    where, when, why, and how to use each.

    CompletenessCreate complete documentation;document all features and functionality.

  • 7/29/2019 Code Quality Etc. IDI Outline

    7/71

    7

    Code Standards (3)

    General rules (continued):

    ConsistencySimilar entities should look andbehave the same; dissimilar entities should look and

    behave differently. Create and apply standardswhenever possible.

    RobustnessProvide predictable documentedbehaviour in response to errors and exceptions. Do

    not hide errors and do not force clients to detecterrors.

  • 7/29/2019 Code Quality Etc. IDI Outline

    8/71

    8

    Code Standards (4)

    Do it right the first time !

    Yourprofessionalism is expressed by applying

    code standards ! Document any deviations!

  • 7/29/2019 Code Quality Etc. IDI Outline

    9/71

    9

    Formatting

    Is important forreadability, not for the

    compiler.

    Use a common standard for code formatting. Do notalter the style of old code to fit new

    standards.

  • 7/29/2019 Code Quality Etc. IDI Outline

    10/71

    10

    Examples (1)

    class MyClass {

    void function (int arg) {

    if (arg < 0) {

    for (int index = 0; index

  • 7/29/2019 Code Quality Etc. IDI Outline

    11/71

    11

    Examples (2)

    Include white space:

    Bad: double length=Math.sqrt(x*x+y*y);

    Better: double length = Math.sqrt(x * x + y * y); Use blank lines to separate.

    Do not use hard tabs.

  • 7/29/2019 Code Quality Etc. IDI Outline

    12/71

    12

    Naming

    Clear, unambiguous, readable, meaningful.Describe the purpose of the item:

    Bad: X1, X2, mth, get, tmp, temp, result.

    Give a descriptive name to temporary variables.

    But: scientific formulas may be betterformulated with single characters/wordsrepresenting symbols instead of descriptivenames.

  • 7/29/2019 Code Quality Etc. IDI Outline

    13/71

    13

    Naming

    Establish and use a common naming convention.

    Problems creating a good name purpose of the

    operation is not clear.

    Bad: void get()., better: retrieveDataSamples.

    Bad: Time day(Time p_day), better: getDate or

    getTruncDate.

    Bad: void result(), better: createResults.

    Bad: void gas/oil/water, better: calculateVolumeRate.

  • 7/29/2019 Code Quality Etc. IDI Outline

    14/71

    14

    Java Naming Convention

    Package: scope.mypackage

    Classes: MyClass

    Methods: myMethod Constants: MY_CONSTANT

    Attribute: myAttribute

    Variable: myVariable

  • 7/29/2019 Code Quality Etc. IDI Outline

    15/71

    15

    Parameters

    Actual parameters should match the formal

    Input-modify-outputorder

    If several operations use similar parameters, put the

    similar parameters in a consistent order

    Use all parameters

    Document interface assumptions about parameters

    access type, unit, ranges, non-valid values Limit the number of parameters to about seven

  • 7/29/2019 Code Quality Etc. IDI Outline

    16/71

    16

    Comments; Why, when, where,

    what Why: To be able to find out what a operation does

    after a half, one or two years. Automatic APIdocumentation.

    When; Document your code before or when you writeit; Design before you implement. Put the design in theoperation.

    Where; Before the operation, at specific formulas,decision points etc.

    What; Document the algorithm, avoid unnecessarycomments. Refer to a specification if existing.

  • 7/29/2019 Code Quality Etc. IDI Outline

    17/71

    17

    JavaDoc (1)

    Generates HTML-formatted class reference or API

    documentation.

    Does only recognise documentation comments that

    appear immediately before class

    Interface

    constructor,

    method

    field declaration.

  • 7/29/2019 Code Quality Etc. IDI Outline

    18/71

    18

    JavaDoc (2)

    Purpose: To define a programmingcontract between

    a clientand a supplier of aservice.

    Keep the documentation synchronised with the code.

    MAKE DOCUMENTATION FIRST, then code!

    JavaDoc tags:

    @author, @version, @see, @param, @return, @exception.

    {@link}.

  • 7/29/2019 Code Quality Etc. IDI Outline

    19/71

    19

    JavaDoc Example: Class

    /**

    * MyConstants is the base class for all the constants and

    * implements any general constant responsibilities.

    *

    * @author Carl-Fredrik Srensen

    * @version $Revision: 1.0$

    * @invariants p > 0

    *

    * @see MySpecificConstants

    */

  • 7/29/2019 Code Quality Etc. IDI Outline

    20/71

    20

    JavaDoc (3)

    Document pre-, post- and invariant conditions

    (OCL).

    @pre-condition, @post-condition, @invariants.

    Document known defects and deficiencies.

    @defect.

    Document synchronisation semantics.

  • 7/29/2019 Code Quality Etc. IDI Outline

    21/71

    21

    JavaDoc Example: Method

    /**

    * Method description

    *

    * @param paramName Name of the mechanism to search for,

    * one of the constants in the MyClass class.

    * @return The concrete instance of an MyClass

    * that is currently in effect if one found, null if not.

    * @exception Throwable Default finalizer exception.

    * @pre-condition valid paramName.

    * @post-condition ( described in @return here)

    */

  • 7/29/2019 Code Quality Etc. IDI Outline

    22/71

    22

    Architecture; How to Avoid

    Spider-web Class/package organisation (loose coupling, high

    cohesion):

    Split classes in (package/service) layers (user, data and

    business). Use package scoping (no.ntnu.idi..).

    Uni-directional dependencies.

  • 7/29/2019 Code Quality Etc. IDI Outline

    23/71

    23

    Refactor to a New Architecture

    ::EcBpStreamFlare

    ::EcDpStream ::EcDpWellReservoir

    ::EcBpStreamFluid::EcBpStreamShipper ::EcDpWellStream

    ::EcDpStreamFluid::Theoretical

    ::EcBpStream::EcDpDerivedStream

    ::calc_stream

    ::comp_analysis

  • 7/29/2019 Code Quality Etc. IDI Outline

    24/71

    24

    Information Hiding Do not expose operations that should be local to a

    package.

    Hide data access from the business services.

    Create separate packages for performing data services.

    Define interfaces for data access, like e.g. EJB. Scoping:

    public: classes exposed by interfaces, operations ininterfaces.

    package: classes and operation not exposed throughinterface, but used by other classes in package.

    private: local attributes and methods.

  • 7/29/2019 Code Quality Etc. IDI Outline

    25/71

    25

    Information Hiding

    Hide the action part in control structures (functional

    cohesion) if complex, else delegate to a method.

    What to hide:

    Areas that are likely to change; hardware dependencies,input/output, non-standard language features, difficult design

    and implementation areas, data-size constraints, business

    rules, potential changes.

    Complex data. Complex logic.

  • 7/29/2019 Code Quality Etc. IDI Outline

    26/71

    26

    Binding

    Bind constants as late as possible

    Do not use magic Numbers, avoid hard-coded valuestotalFooDay = totalFooHour * 24;

    if (me.equals(thirsty)) return water;

    Avoid global variables (constants OK)

    Use separate classes/methods to hide hard-coded values

    Achieves faster maintenance, and avoids copy-paste errors

    Makes code better suited for reuse

    Static methods and/or constants

    MyConstants.C1_SPECIFIC_GRAVITY

  • 7/29/2019 Code Quality Etc. IDI Outline

    27/71

    27

    Java Exceptions (1)

    Uncheckedrun-time exception: serious

    unexpected errors that may indicate an error in

    the programs logic Termination.

    Checkedexception: errors that may occur,

    however rarely, under normal program

    operation The caller must catch this

    exception.

  • 7/29/2019 Code Quality Etc. IDI Outline

    28/71

    28

    Java Exceptions (2)

    Only convert exceptions to add information. If

    the method does not know how to handle an

    exception it should not be handled.

    Do not silently absorb a run-time or error

    exception makes code very hard to debug.

    Use finally blocks to release resources.

  • 7/29/2019 Code Quality Etc. IDI Outline

    29/71

    29

    Code Defensively (1)

    Check input data for validity (Pre-conditions). Range, comment assumptions about acceptable input ranges

    in the code.

    Use a general approach for error handling when erroneousdata.

    Use exception handling only to draw attention tounexpected cases (Do NOT perform any processing inexception code) (invariants).

    Anticipate changes; Hide to minimise impact ofchange.

  • 7/29/2019 Code Quality Etc. IDI Outline

    30/71

    30

    Code Defensively (2)

    Introduce debugging aids early (logging).

    Check function return values (post-conditions).

    Return friendly error messages; Write to a logfile any system specific error messages

    (IO/SQL Exceptions, error codes etc.).

  • 7/29/2019 Code Quality Etc. IDI Outline

    31/71

    31

    Summary

    Remember your code should be understandable.

    Maintenance is often up to 70% of a total

    project cost.

    Use quality control.

  • 7/29/2019 Code Quality Etc. IDI Outline

    32/71

    32

    Outline

    Code quality and Code Standards.

    Debugging, logging etc.

    Testing.

  • 7/29/2019 Code Quality Etc. IDI Outline

    33/71

    33

    Debugging

    Single thread/process. IDEs with debugger most often sufficient.

    Multiple clients, threads, distributed applications.

    Synchronisation issues to protect the state of objects. IDEs (most often) lack good support.

    Pre-instrumentation of code is often necessary.

    Non-visual services (e.g. Real-time data conversions).

    May debug single runs in IDEs. Hard to debug in real settings: system runs continuously ordiscretely at fixed times

  • 7/29/2019 Code Quality Etc. IDI Outline

    34/71

    34

    Application Logging: What

    and Why? An application log is a text history of notable events

    logged by your application.

    The logs helps you to figure out what went wrong (and

    right) during the execution of your application. With the advent of N-tier architectures, Servlets, JSPs,

    and EJBs, application logging is particularly important

    to report errors that cannot or should not be surfaced to

    the user interface.

  • 7/29/2019 Code Quality Etc. IDI Outline

    35/71

    35

    JLog Java Logging Framework

    Developed by Todd Lauinger.

    A significant event happens in your Java code.

    That event could be any of several different

    types and conditions. Before you start using a

    logging framework:

    categorize and document your events so that all

    users of the framework will log events consistently.

  • 7/29/2019 Code Quality Etc. IDI Outline

    36/71

    36

    System Event Categories

    Levels:

    CRITICAL_ERROR. (Highest - 1)

    ERROR.

    WARNING.

    INFORMATION.

    DETAIL.

    TRACE. (Lowest - 6)

  • 7/29/2019 Code Quality Etc. IDI Outline

    37/71

    37

    Logging Granularity

    Agreed and documented set of event categories

    determine the granularity to log those events.

    Separate logs for e.g.

    thread pool.

    SQL processor.

  • 7/29/2019 Code Quality Etc. IDI Outline

    38/71

    38

    Logging Events

    Instrument code with logging statements:

    AppLog.criticalError("Caught unexpected

    exception: " + e);

    SQLLog.info("Executing SQL query: " +statement);

    AppLog.trace("Entering method getName()");

    Notice: the code does not need to have any "if"logic.

  • 7/29/2019 Code Quality Etc. IDI Outline

    39/71

    39

    Configuring the Logs (1)

    Configuration from a properties file.LogFileExtension = log

    LogFilePath = c:\\temp\\

    LoggingLevel = 2LoggingMechanism = log.StandardErrLoggingMechanism

    LogFieldSeparator = |

  • 7/29/2019 Code Quality Etc. IDI Outline

    40/71

    40

    Configuring the Logs (2)

    You may select the output logging level.

    Default is the INFO level.

    All events logged at a level less than or equal to

    the log's current logging level will be output to

    the logging mechanisms.

    Events logged at a numerically higher level

    (i.e., a less critical level) will be discarded.

  • 7/29/2019 Code Quality Etc. IDI Outline

    41/71

    41

    Configuring the Logs (3)

    At runtime, you can increase ordecrease the

    logging level of any of your logs without

    affecting your other logs.

    If you are trying to debug a nasty problem with

    your thread pool, you can programmatically

    change the log at runtime :ThreadLog.getInstance().setLoggingLevel(Log.TRACE_LEVEL);

  • 7/29/2019 Code Quality Etc. IDI Outline

    42/71

    42

    Configuring the Logs (4)

    Other ways to dynamically reset the logging level at

    runtime:

    In a debugger, you can change the value of the log's

    currentLoggingLevel variable. In an application server, you can examine and manipulate log

    properties with some JSPs.

    Use RMI to manipulate the log properties of a remote JVM.

    There are more options you can configure both

    programmatically and via a properties file.

  • 7/29/2019 Code Quality Etc. IDI Outline

    43/71

    43

    Reading the Logs (1)

    Sample entries from a shared log, a vertical

    bar ( | ), is used to delimit the various fields

    of log entries:RequestLog|L4|09:32:23:769|ExecuteThread-5|Executing

    request number 4

    SQLLog|L4|09:32:23:835|ExecuteThread-5|select * from

    Customer where id = 35.

    RequestLog|L4|09:32:23:969|ExecuteThread-5|Request 4

    took 200 milliseconds.

  • 7/29/2019 Code Quality Etc. IDI Outline

    44/71

    44

    Reading the Logs (2)

    Import the log into a spreadsheet:ASCII text import with a vertical bar as a field

    delimiter.

    Sort or filter the log using various spreadsheet

    capabilities.

    P i JL Cl d

  • 7/29/2019 Code Quality Etc. IDI Outline

    45/71

    45

    Primary JLog Classes and

    Relationships

  • 7/29/2019 Code Quality Etc. IDI Outline

    46/71

    46

    Outline

    Code quality and Code Standards.

    Debugging, logging etc.

    Testing.

  • 7/29/2019 Code Quality Etc. IDI Outline

    47/71

    47

    Testing

    Not closely integrated with development prevents

    measurement of the progress of development - can't

    tell when something starts working or when something

    stops working. JUnitto cheaply and incrementally build a test suite

    that helps to:

    measure your progress,

    spot unintended side effects. focus your development efforts.

  • 7/29/2019 Code Quality Etc. IDI Outline

    48/71

    48

    JUnit

    Automatic testing framework.

    Acceptance tests.

    Integration test.

    Unit test.

    Reports the number of defects graphically.

    May create many tests for each method.

  • 7/29/2019 Code Quality Etc. IDI Outline

    49/71

    49

    JUnit Example

    Pay attention to the interplay of the code andthe tests.

    The style: to write a few lines of code, then a test

    that should run, or even better: to write a test that won't run, then

    write the code that will make it run.

    The program presented solves the problem of

    representing arithmetic with multiplecurrencies.

  • 7/29/2019 Code Quality Etc. IDI Outline

    50/71

    50

    Example: Moneyclass Money {

    private int fAmount;

    private String fCurrency;

    public Money(int amount, String currency) {

    fAmount= amount

    fCurrency= currency;

    }public int amount() {

    return fAmount;

    }

    public String currency() {

    return fCurrency;}

    }

  • 7/29/2019 Code Quality Etc. IDI Outline

    51/71

    51

    JUnit

    JUnit defines how to structure your test cases

    and provides the tools to run them.

    You implement a test in a subclass ofTestCase.

  • 7/29/2019 Code Quality Etc. IDI Outline

    52/71

    52

    Example: Money

    public Money add(Money m) {

    return new Money(amount()+m.amount(), currency());

    }

  • 7/29/2019 Code Quality Etc. IDI Outline

    53/71

    53

    Junit

    Define MoneyTest as a subclass ofTestCase. Put MoneyTest in the same package as the

    classes under test access to the package

    private methods. Add method testSimpleAdd, that will exercise the

    simple version ofMoney.add() above.

    A JUnit test method is an ordinary method without

    any parameters.

  • 7/29/2019 Code Quality Etc. IDI Outline

    54/71

    54

    Example: MoneyTest

    public class MoneyTest extends TestCase {

    //

    public void testSimpleAdd() {

    Money m12CHF= new Money(12, "CHF"); // (1)

    Money m14CHF= new Money(14, "CHF");Money expected= new Money(26, "CHF");

    Money result= m12CHF.add(m14CHF); // (2)

    assert(expected.equals(result)); // (3)

    }

    }

  • 7/29/2019 Code Quality Etc. IDI Outline

    55/71

    55

    Developing Tests

    public void testEquals() {

    Money m12CHF= new Money(12, "CHF");

    Money m14CHF= new Money(14, "CHF");

    assert(!m12CHF.equals(null));

    assertEquals(m12CHF, m12CHF);assertEquals(m12CHF, new Money(12, "CHF")); // (1)

    assert(!m12CHF.equals(m14CHF));

    }

  • 7/29/2019 Code Quality Etc. IDI Outline

    56/71

    56

    Developing Testspublic boolean equals(Object anObject) {

    if (anObject instanceof Money) {

    Money aMoney= (Money)anObject;

    return aMoney.currency().equals(currency())

    && amount() == aMoney.amount();

    }return false;

    }

    Override the method hashCode whenever you override method

    equals.

  • 7/29/2019 Code Quality Etc. IDI Outline

    57/71

    57

    Assertions

    Verification in JUnit by calling assert which is

    inherited from TestCase.

    Assert triggers a failure that is logged by JUnit when

    the argument isn't true. Since assertions for equality are very common,

    TestCase defines an assertEquals convenience method.

    Logs the printed value of the two objects if they differ.

    Shows why a test failed in a JUnit test result report.Logged as a string representation created by toString.

  • 7/29/2019 Code Quality Etc. IDI Outline

    58/71

    58

    Test Fixture

    public class MoneyTest extends TestCase {

    private Money f12CHF;

    private Money f14CHF;

    protected void setUp() {

    f12CHF= new Money(12, "CHF");f14CHF= new Money(14, "CHF");

    }

    }

    T R f d

  • 7/29/2019 Code Quality Etc. IDI Outline

    59/71

    59

    Tests Refactored

    public void testEquals() {assert(!f12CHF.equals(null));

    assertEquals(f12CHF, f12CHF);

    assertEquals(f12CHF, new Money(12, "CHF"));

    assert(!f12CHF.equals(f14CHF));

    }public void testSimpleAdd() {

    Money expected= new Money(26, "CHF");

    Money result= f12CHF.add(f14CHF);

    assert(expected.equals(result));

    }

    R i f T t

  • 7/29/2019 Code Quality Etc. IDI Outline

    60/71

    60

    Running of Tests

    Two additional steps are needed to run the two

    test cases:

    1. define how to run an individual test case,

    2. define how to run a test suite.

    JUnit supports two ways of running single tests:

    static.

    dynamic.

    T t C St ti

  • 7/29/2019 Code Quality Etc. IDI Outline

    61/71

    61

    Test Case: Static

    Overrides the runTest method inherited from TestCase andcall the desired test case.

    Convenient way: anonymous inner class.

    Note: each test must be given a name to identify it if it fails.

    TestCase test = new MoneyTest("simple add") {public void runTest() {

    testSimpleAdd();

    }

    };

    A template method in the super-class will make sure runTest isexecuted when the time comes.

    Dynamic: TestCase test = newMoneyTest("testSimpleAdd");

    T t S it D i

  • 7/29/2019 Code Quality Etc. IDI Outline

    62/71

    62

    Test Suite: Dynamic

    Illustration of the creation of a test suite with

    the dynamic way to run a test:

    You only pass the class with the tests to a TestSuite

    and it extracts the test methods automatically.

    public static Test suite() {return new TestSuite(MoneyTest.class);

    }

    T t S it St ti

  • 7/29/2019 Code Quality Etc. IDI Outline

    63/71

    63

    Test Suite: Staticpublic static Test suite() {

    TestSuite suite= new TestSuite();suite.addTest(new MoneyTest("money equals"){

    protected void runTest() {

    testEquals();

    }

    }

    );suite.addTest(new MoneyTest("simple add") {

    protected void runTest() {

    testSimpleAdd();

    }

    }

    );

    return suite;

    }

  • 7/29/2019 Code Quality Etc. IDI Outline

    64/71

    64

    JUnit Re ie (1)

  • 7/29/2019 Code Quality Etc. IDI Outline

    65/71

    65

    JUnit Review (1)

    In general: development will go much smoother

    writing tests a little at a time when developing.

    When coding the imagination of how the code

    will work. Capture the thoughts in a test.

    Test code is just like model code in working

    best if it is factored well.

    JUnit Review (2)

  • 7/29/2019 Code Quality Etc. IDI Outline

    66/71

    66

    JUnit Review (2)

    Keeping old tests running is just as important as

    making new ones run.

    The ideal is to always run all of your tests.

    When you are struck by an idea, defer thinking

    about the implementation. First write the test.

    Then run it. Then work on the implementation.

    Testing Practices (1)

  • 7/29/2019 Code Quality Etc. IDI Outline

    67/71

    67

    Testing Practices (1)

    Martin Fowler: "Whenever you are tempted totype something into a print statement or adebugger expression, write it as a test instead.

    Only a fraction of the tests are actually useful. Write tests that fail even though they should work,

    or tests that succeed even though they should fail.

    Think of it is in cost/benefit terms. You want tests

    that pay you back with information.

    Testing Practices (2)

  • 7/29/2019 Code Quality Etc. IDI Outline

    68/71

    68

    Testing Practices (2)

    Receive a reasonable return on your testinginvestment:

    During Development.

    During Debugging.

    Caution: Once you get them running, make sure they stay running.

    Ideally, run every test in the suite every time youchange a method. Practically, the suite will soon grow

    too large to run all the time.

    Testing Practices (3)

  • 7/29/2019 Code Quality Etc. IDI Outline

    69/71

    69

    Testing Practices (3)

    Try to optimise your set-up code so you can run all the

    tests.

    Or,

    create special suites that contain all the tests that mightpossibly be affected by your current development.

    run the suite every time you compile.

    make sure you run every test at least once a day: overnight,

    during lunch, during one of those long meetings.

    References

  • 7/29/2019 Code Quality Etc. IDI Outline

    70/71

    70

    References

    Code Complete; Steve McConnell, Microsoft

    Press.

    The Elements of Java Style; Allan Vermeulen,

    SIGS Reference Library, Cambridge http://www.javareport.com/

    http://www.catapult-technologies.com/whitepapers/jlog/

    http://www.junit.org/

    Questions

    http://www.javareport.com/html/from_pages/oldarticles.asp?ArticleID=966http://www.catapult-technologies.com/whitepapers/jlog/http://www.junit.org/http://www.junit.org/http://www.catapult-technologies.com/whitepapers/jlog/http://www.catapult-technologies.com/whitepapers/jlog/http://www.catapult-technologies.com/whitepapers/jlog/http://www.javareport.com/html/from_pages/oldarticles.asp?ArticleID=966
  • 7/29/2019 Code Quality Etc. IDI Outline

    71/71

    71

    Questions