#all java code is spaghetti code to me
Explore tagged Tumblr posts
hotfudgecherryrosy · 2 years ago
Text
im tired of scrolling through my code because java sucks
im going to use evil programming methods
edit: predictably, this broke my code
79 notes · View notes
nickgerlich · 7 months ago
Text
No Skimping
Yesterday’s look at shrinkflation was a necessary discussion, since it is something that consumers can—and should—be able to detect on their own. With little or no sleuthing, any one of us can either read or calculate per unit pricing. The results may not exactly be good news, but what choice do we have other than simply not buying the product?
Besides, we can rest assured that if and when the factors of production come down, you can bet that the manufacturers will reverse their strategy and say “Now! 33% More Free!” The product will be back to its usual size, but still at the same price. Color me cynical. I’ve seen it too many times.
But then there’s another crazy thing going on in some quarters, and it is far more devious than any shrinking of the package. This one is much harder to detect, although there are times when you might notice the change. It is called “skimpflation,” and is the result of a company cutting corners. How we consumers react once we do find out is the behavioral aspect to consider.
And one company is making headlines recently because of it, one you probably never thought would do such a thing. Whole Foods, the healthy and pricey alternative to Kroger, Walmart, and the rest, was busted for making some major changes to its Berry Chantilly Cake, and consumers took them to task on it across social media.
Tumblr media
Whole Foods went on to issue an official statement, saying they had “aligned the flavor profile, size, packaging and price” of the cake slices. Ostensibly, this move was made to standardize the product offering across their more than 500 stores.
That’s corporate BS for “We cheapened the product so we could make more money.”
The temptation is very real for companies to look for cheaper ingredients. In this case, replacing fresh fruit and cream with a jammy compote was pretty obvious, but in other cases, you would have to do a deep dive into the ingredient list. Substituting one type of oil for another may appear innocuous, but may also bring cost savings to the maker.
Major breweries have already been doing this for years, using “adjuncts” in the brewing process, which is code for substituting rice and corn for more expensive barley malt. They can all be used to brew beer, but German beer purists would have a heart attack if they knew. They didn’t pass the Reinheitsgebot, or Beer Purity Law, in 1516 for nothing. Heck, they didn’t even allow yeast to be added to the barley, hops, and water until 1906.
You may not be enough of a coffee snob to detect when the java you are drinking is sometimes a blend of Arabica and Robusta. It dilutes product quality, because Arabica—which is more expensive—is highly regarded for its smooth taste. But Robusta stretches out the supply of the better beans, little different from adding a little water to the spaghetti sauce when unexpected guests show up at dinner time.
Companies who do tweak their formulations do so at great risk. If consumers find out and don’t like it, they can easily go viral with their social media outbursts. It’s not at all like things were before the internet gave everyone a microphone.
Skimpflation also extends into the service sector. I have seen it especially at hotels. If you have a multi-day stay, you probably won’t see housekeeping unless you specifically request it. And those toiletries once found in every room have been replaced by pump dispensers mounted on the wall in the shower. While this practice originated in California because of a law intended to curb landfill waste, it quickly spread across the nation as hoteliers realized that they could do things more cheaply.
As for Whole Foods, they caved and are returning their popular cake to its original ingredients. Good for them. But happy endings like this seldom happen, because once a product has been modified, it usually stays that way.
Do any of you remember reading about the New Coke fiasco of 1985? Following an uproar over an announced product change, the company then brought back original Coca-Cola, which they called Coca-Cola Classic. But what they did not tell anyone is that the revived product had one major difference: they substituted high fructose corn syrup for regular cane sugar. Corn is in abundance in the US, and is much cheaper as a sugar than the real stuff.
They’re still doing it, and I don’t hear anyone complaining.
The jury of public opinion has to weigh in on these matters one by one. In the case of Whole Foods, their well-off customer base complained loudly, and they were heard. That may not always be the case for other products. And we just have to along with it, or find something else to buy.
Dr “No Rice In My Beer” Gerlich
Audio Blog
0 notes
brophyblam · 4 years ago
Text
Writing Contest Runner-up
Congrats to John Gauci ‘21 on winning third place with his piece titled “A Computing Activity.” 
A Computing Activity 
Programming was one of those things that always seemed to captivate my interests. Ever since I was in middle school and was becoming rapidly acquainted with the workings of computers, fabricating the software that millions of people rely on for business applications like cybersecurity and engineering seemed like a natural consequence of my growing propensity for technology. 
Of course, interests are not the ice cubes that never get picked out of the freezer. Rather, they are the vines growing down concrete, where the general direction is known, but the small branch-offs help to shape the idea of the whole. High school was definitely the sort of fertilizer that helped this growth. 
Firstly, it threw me headfirst into object oriented programming with learning Java. Instead of serving steaming hot plates of spaghetti code that appealed to my middle school cerebrum, I was constrained to construct software that had a logical flow to it. 
Next, in middle school, I found math to be pretty neat. But in high school, math became interesting with its adulteration of the what were once forbidden concepts of zeroes and infinities. And it became clear that this stuff could no longer be done on the accountant’s calculator. 
So that is how I found myself trying to program a computer algebra system in C++. 
A computer algebra system crosses the canyon between human and computer mathematical capabilities by giving the calculator ways to perform certain algebraic manipulations. However, the challenge in designing the skeleton for this system is not a trivial one. In most cases when you are asked to solve a problem for a class, your mind stares down a plastic tube. You stick to what you know, what you have been taught, and even when you deviate, you can see if you are going to clear the trees before you land. However, with a project like this, you are not just finding an answer, you are building the solution. 
Due to this, I learned that the most important part of programming is not sitting in a chair, staring at the blistering white or charcoal gray themed development environment. Rather, it is the type of rhythm that is created while I pace about mulling over how to jump over the infinitely tall
hurdle and eavesdropping in on the pattering of my feet slapping the marble floor, navigating the possible ways to synthesize human ingenuity with technological prowess. I get all these ambitious ideas: What if I did this? No, how about that. Well not exactly, but if I changed that little part, maybe? Nah, too complicated. What about this… And then, not too long later 
Reality hits. I am only a novice, and I have no serious qualifications. I have to settle on something feasible, within my limits. The desires of piecing together this intricate, complex, flexible, and optimally performing software subside. Perhaps one of the more irking parts of this process is wishing I was more experienced and capable, and could have fleshed out an idea that was more formidable when I first came across it. 
Nevertheless, I transition into the second step of actually pouring out whatever idea I had in my head into the keyboard and onto the computer. From this point, everything seems to flow downstream. Sure, it involves some thinking, and there are hitches and rocks also in this stream, but these are small compared to picking which river to cruise down in. The actual working of the code can sometimes be tedious, but finishing up a certain functionality always gives a sense of reward and satisfaction. However, the sense I get is a hollow sense, for the most difficult part is yet to come. 
Here is probably where I spend most of my time “programming”. After all, putting the effort into typing up the program is one thing, making it work is another. 
And so the frustration ensues. For one thing, C++ drops off the training wheels and pins the effort of managing the program’s memory onto the programmer. Therefore, I do check to make sure my program is not seeping out memory that could, in theory, cause serious issues. After all, I do not want the software to have a leaky roof should a rainstorm come through. Though combined with my inexperience, and this process of ensuring good code results in what I would say is an error fest. Running through the code, line by line, and methodically checking the changes of the values of multiple variables to find out where I accessed some bit of memory that was off limits not only requires a keen eye, but determination and patience as you miss the one minute consequence of a single entry in a sea of code not once or twice, but ten times over. Then I realize that this is only half of the battle, and my code also has to do what I want it to do (which it doesn’t). I continue on with the next stage of this demanding process. 
The clacks of the mechanical keyboard are the only thing that is heard. A pause. Silence accompanied by quiet breathing. Click! The code stops executing on a line that has a little red stop sign next to it. The addresses of variables change. Click! Stopsign. 0x005AF8C0 Click! Memory pointers swapped around like a telephone switchboard. Countless calculations and computations occurring under the hood. Then it all comes to a screeching halt. “Exception
thrown: read access violation”. The clicks stop. No keys are pressed. The air is filled with a soft sigh. The cycle repeats.
0 notes
nancydsmithus · 6 years ago
Text
Testing Made Easier Via Framework Minimalism And Software Architecture
Testing Made Easier Via Framework Minimalism And Software Architecture
Ryan Kay
2019-08-22T13:00:59+02:002019-08-22T11:05:42+00:00
Like many other Android developers, my initial foray into testing on the platform lead me to be immediately confronted with a demoralizing degree of jargon. Further, what few examples I came across at the time (circa 2015) did not present practical use cases which may have inclined me to think that the cost to benefit ratio of learning a tool like Espresso in order to verify that a TextView.setText(…) was working properly, was a reasonable investment.
To make matters even worse, I did not have a working understanding of software architecture in theory or practice, which meant that even if I bothered to learn these frameworks, I would have been writing tests for monolithic applications comprised of a few god classes, written in spaghetti code. The punchline is that building, testing, and maintaining such applications is an exercise in self-sabotage quite regardless of your framework expertise; yet this realization only becomes clear after one has built a modular, loosely-coupled, and highly-cohesive application.
From here we arrive at one of the main points of discussion in this article, which I will summarize in plain language here: Among the primary benefits of applying the golden principles of software architecture (do not worry, I will discuss them with simple examples and language), is that your code can become easier to test. There are other benefits to applying such principles, but the relationship between software architecture and testing is the focus of this article.
However, for the sake of those who wish to understand why and how we test our code, we will first explore the concept of testing by analogy; without requiring you to memorize any jargon. Before getting deeper into the primary topic, we will also look at the question of why so many testing frameworks exist, for in examining this we may begin to see their benefits, limitations, and perhaps even an alternative solution.
Testing: Why And How
This section will not be new information for any seasoned tester, but perhaps you may enjoy this analogy nonetheless. Of course I am a software engineer, not a rocket engineer, but for a moment I will borrow an analogy which relates to designing and building objects both in physical space, and in the memory space of a computer. It turns out that while the medium changes, the process is in principle quite the same.
Suppose for a moment that we are rocket engineers, and our job is to build the first stage* rocket booster of a space shuttle. Suppose as well, that we have come up with a serviceable design for the first stage to begin building and testing in various conditions.
“First stage” refers to boosters which are fired when the rocket is first launched
Before we get to the process, I would like to point out why I prefer this analogy: You should not have any difficulty answering the question of why we are bothering to test our design before putting it in situations where human lives are at stake. While I will not try to convince you that testing your applications before launch could save lives (although it is possible depending on the nature of the application), it could save ratings, reviews, and your job. In the broadest sense, testing is the way in which we make sure that single parts, several components, and whole systems work before we employ them in situations where it is critically important for them to not fail.
Returning to the how aspect of this analogy, I will introduce the process by which engineers go about testing a particular design: redundancy. Redundancy is simple in principle: Build copies of the component to be tested to the same design specification as what you wish to use at launch time. Test these copies in an isolated environment which strictly controls for preconditions and variables. While this does not guarantee that the rocket booster will work properly when integrated in the whole shuttle, one can be certain that if it does not work in a controlled environment, it will be very unlikely to work at all.
Suppose that of the hundreds, or perhaps thousands of variables which the copies of the rocket design have been tested against, it comes down to ambient temperatures in which the rocket booster will be test fired. Upon testing at 35° Celsius, we see that everything functions without error. Again, the rocket is tested at roughly room temperature without failure. The final test will be at the lowest recorded temperature for the launch site, at -5° Celcius. During this final test, the rocket fires, but after a short period, the rocket flares up and shortly thereafter explodes violently; but fortunately in a controlled and safe environment.
At this point, we know that changes in temperature appear to be at least involved in the failed test, which leads us to consider what parts of the rocket booster may be adversely effected by cold temperatures. Over time, it is discovered that one key component, a rubber O-ring which serves to staunch the flow of fuel from one compartment to another, becomes rigid and ineffectual when exposed to temperatures approaching or below freezing.
It is possible that you have noticed that his analogy is loosely based on the tragic events of the Challenger space shuttle disaster. For those unfamiliar, the sad truth (insofar as investigations concluded) is that there were plenty of failed tests and warnings from the engineers, and yet administrative and political concerns spurred the launch to proceed regardless. In any case, whether or not you have memorized the term redundancy, my hope is that you have grasped the fundamental process for testing parts of any kind of system.
Concerning Software
Whereas the prior analogy explained the fundamental process for testing rockets (while taking plenty of liberty with the finer details), I will now summarize in a manner which is likely more relevant to you and I. While it is possible to test software by only launching it to devices once it is in any sort of deployable state, I suppose instead that we can apply the principle of redundancy to the individual parts of the application first.
This means that we create copies of the smaller parts of the whole application (commonly referred to as Units of software), set up an isolated test environment, and see how they behave based on whatever variables, arguments, events, and responses which may occur at runtime. Testing is truly as simple as that in theory, but the key to even getting to this process lies in building applications which are feasibly testable. This comes down to two concerns which we will look at in the next two sections. The first concern has to do with the test environment, and the second concern has to do with the way in which we structure applications.
Why Do We Need Frameworks?
In order to test a piece of software (henceforth referred to as a Unit, although this definition is deliberately an over-simplification), it is necessary to have some kind of testing environment which allows you to interact with your software at runtime. For those building applications to be executed purely on a JVM (Java Virtual Machine) environment, all that is required to write tests is a JRE (Java Runtime Environment). Take for example this very simple Calculator class:
class Calculator { private int add(int a, int b){ return a + b; } private int subtract(int a, int b){ return a - b; } }
In absence of any frameworks, as long as we have a test class which contains a mainfunction to actually execute our code, we can test it. As you may recall, the main function denotes the starting point of execution for a simple Java program. As for what we are testing for, we simply feed some test data into the Calculator’s functions and verify that it is performing basic arithmetic properly:
public class Main { public static void main(String[] args){ //create a copy of the Unit to be tested Calculator calc = new Calculator(); //create test conditions to verify behaviour int addTest = calc.add(2, 2); int subtractTest = calc.subtract(2, 2); //verify behaviour by assertion if (addTest == 4) System.out.println("addTest has passed."); else System.out.println("addTest has failed."); if (subtractTest == 0) System.out.println("subtractTest has passed."); else System.out.println("subtractTest has failed."); } }
Testing an Android application is of course, a completely different procedure. Although there is a main function buried deep within the source of the ZygoteInit.java file (the finer details of which are not important here), which is invoked prior to an Android application being launched on the JVM, even a junior Android developer ought to know that the system itself is responsible for calling this function; not the developer. Instead, the entry points for Android applications happen to be the Application class, and any Activity classes which the system can be pointed to via the AndroidManifest.xml file.
All of this is just a lead up to the fact that testing Units in an Android application presents a greater level of complexity, strictly because our testing environment must now account for the Android platform.
Taming The Problem Of Tight Coupling
Tight coupling is a term which describes a function, class, or application module which is dependent on particular platforms, frameworks, languages, and libraries. It is a relative term, meaning that our Calculator.java example is tightly coupled to the Java programming language and standard library, but that is the extent of its coupling. Along the same lines, the problem of testing classes which are tightly coupled to the Android platform, is that you must find a way to work with or around the platform.
For classes tightly coupled to the Android platform, you have two options. The first, is to simply deploy your classes to an Android device (physical or virtual). While I do suggest that you test deploy your application code before shipping it to production, this is a highly inefficient approach during the early and middle stages of the development process with respect to time.
A Unit, however technical a definition you prefer, is generally thought of as a single function in a class (although some expand the definition to include subsequent helper functions which are called internally by the initial single function call). Either way, Units are meant to be small; building, compiling, and deploying an entire application to test a single Unit is to miss the point of testing in isolation entirely.
Another solution to the problem of tight coupling, is to use testing frameworks to interact with, or mock (simulate) platform dependencies. Frameworks such as Espresso and Robolectric give developers far more effective means for testing Units than the previous approach; the former being useful for tests run on a device (known as “instrumented tests” because apparently calling them device tests was not ambiguous enough) and the latter being capable of mocking the Android framework locally on a JVM.
Before I proceed to railing against such frameworks instead of the alternative I will discuss shortly, I want to be clear that I do not mean to imply that you should never use these options. The process which a developer uses to build and test their applications should be born of a combination of personal preference and an eye for efficiency.
For those who are not fond of building modular and loosely coupled applications, you will have no choice but to become familiar with these frameworks if you wish to have an adequate level of test coverage. Many wonderful applications have been built this way, and I am not infrequently accused of making my applications too modular and abstract. Whether you take my approach or decide to lean heavily on frameworks, I salute you for putting in the time and effort to test your applications.
Keep Your Frameworks At Arms Length
For the final preamble to the core lesson of this article, it is worth discussing why you might want to have an attitude of minimalism when it comes to using frameworks (and this applies to more than just testing frameworks). The subtitle above is a paraphrase from the magnanimous teacher of software best practices: Robert “Uncle Bob” C. Martin. Of the many gems he has given me since I first studied his works, this one took several years of direct experience to grasp.
Insofar As I understand what this statement is about, the cost of using frameworks is in the time investment required to learn and maintain them. Some of them change quite frequently and some of them do not change frequently enough. Functions become deprecated, frameworks cease to be maintained, and every 6-24 months a new framework arrives to supplant the last. Therefore, if you can find a solution which can be implemented as a platform or language feature (which tend to last much longer), it will tend to be more resistant to changes of the various types mentioned above.
On a more technical note, frameworks such as Espresso and to a lesser degree Robolectric, can never run as efficiently as simple JUnit tests, or even the framework free test from earlier on. While JUnit is indeed a framework, it is tightly coupled to the JVM, which tends to change at a much slower rate than the Android platform proper. Fewer frameworks almost invariably means code which is more efficient in terms of the time it takes to execute and write one or more tests.
From this, you can probably gather that we will now be discussing an approach which will leverage some techniques which allows us to keep the Android platform at arms length; all the while allowing us plenty of code coverage, test efficiency, and the opportunity to still use a framework here or there when the need arises.
The Art Of Architecture
To use a silly analogy, one might think of frameworks and platforms as being like overbearing colleagues who will take over your development process unless you set appropriate boundaries with them. The golden principles of software architecture can give you the general concepts and specific techniques necessary to both create and enforce these boundaries. As we will see in a moment, if you have ever wondered what the benefits of applying software architecture principles in your code truly are, some directly, and many indirectly make your code easier to test.
Separation Of Concerns
Separation Of Concerns is by my estimation the most universally applicable and useful concept in software architecture as a whole (without meaning to say that others should be neglected). Separation of concerns (SOC) can be applied, or completely ignored, across every perspective of software development I am aware of. To briefly summarize the concept, we will look at SOC when applied to classes, but be aware that SOC can be applied to functions through extensive usage of helper functions, and it can be extrapolated to entire modules of an application (“modules” used in the context of Android/Gradle).
If you have spent much time at all researching software architectural patterns for GUI applications, you will likely have come across at least one of: Model-View-Controller (MVC), Model-View-Presenter (MVP), or Model-View-ViewModel (MVVM). Having built applications in every style, I will say upfront that I do not consider any of them to be the single best option for all projects (or even features within a single project). Ironically, the pattern which the Android team presented some years ago as their recommended approach, MVVM, appears to be the least testable in absence of Android specific testing frameworks (assuming you wish to use the Android platform’s ViewModel classes, which I am admittedly a fan of).
In any case, the specifics of these patterns are less important than their generalities. All of these patterns are just different flavours of SOC which emphasize a fundamental separation of three kinds of code which I refer to as: Data, User Interface, Logic.
So, how exactly does separating Data, User Interface, and Logic help you to test your applications? The answer is that by pulling logic out of classes which must deal with platform/framework dependencies into classes which possess little or no platform/framework dependencies, testing becomes easy and framework minimal. To be clear, I am generally talking about classes which must render the user interface, store data in a SQL table, or connect to a remote server. To demonstrate how this works, let us look at a simplified three layer architecture of a hypothetical Android application.
The first class will manage our user interface. To keep things simple, I have used an Activity for this purpose, but I typically opt for Fragments instead as user interface classes. In either case, both classes present similar tight coupling to the Android platform:
public class CalculatorUserInterface extends Activity implements CalculatorContract.IUserInterface { private TextView display; private CalculatorContract.IControlLogic controlLogic; private final String INVALID_MESSAGE = "Invalid Expression."; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); controlLogic = new DependencyProvider().provideControlLogic(this); display = findViewById(R.id.textViewDisplay); Button evaluate = findViewById(R.id.buttonEvaluate); evaluate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { controlLogic.handleInput('='); } }); //..bindings for the rest of the calculator buttons } @Override public void updateDisplay(String displayText) { display.setText(displayText); } @Override public String getDisplay() { return display.getText().toString(); } @Override public void showError() { Toast.makeText(this, INVALID_MESSAGE, Toast.LENGTH_LONG).show(); } }
As you can see, the Activity has two jobs: First, since it is the entry point of a given feature of an Android application, it acts as a sort of container for the other components of the feature. In simple terms, a container can be thought of as a sort of root class which the other components are ultimately tethered to via references (or private member fields in this case). It also inflates, binds references, and adds listeners to the XML layout (the user interface).
Testing Control Logic
Rather than having the Activity possess a reference to a concrete class in the back end, we have it talk to an interface of type CalculatorContract.IControlLogic. We will discuss why this is an interface in the next section. For now, just understand that whatever is on the other side of that interface is supposed to be something like a Presenter or Controller. Since this class will be controlling interactions between the front-end Activity and the back-end Calculator, I have chosen to call it CalculatorControlLogic:
public class CalculatorControlLogic implements CalculatorContract.IControlLogic { private CalculatorContract.IUserInterface ui; private CalculatorContract.IComputationLogic comp; public CalculatorControlLogic(CalculatorContract.IUserInterface ui, CalculatorContract.IComputationLogic comp) { this.ui = ui; this.comp = comp; } @Override public void handleInput(char inputChar) { switch (inputChar){ case '=': evaluateExpression(); break; //...handle other input events } } private void evaluateExpression() { Optional result = comp.computeResult(ui.getDisplay()); if (result.isPresent()) ui.updateDisplay(result.get()); else ui.showError(); } }
There are many subtle things about the way in which this class is designed that make it easier to test. Firstly, all of its references are either from the Java standard library, or interfaces which are defined within the application. This means that testing this class without any frameworks is an absolute breeze, and it could be done locally on a JVM. Another small but useful tip is that all of the different interactions of this class can be called via a single generic handleInput(...) function. This provides a single entry point to test every behaviour of this class.
Also note that in the evaluateExpression() function, I am returning a class of type Optional<String> from the back end. Normally I would use what functional programmers call an Either Monad, or as I prefer to call it, a Result Wrapper. Whatever stupid name you use, it is an object which is capable of representing multiple different states through a single function call. Optional is a simpler construct which can represent either a null, or some value of the supplied generic type. In any case, since the back end might be given an invalid expression, we want to give the ControlLogicclass some means of determining the result of the backend operation; accounting for both success and failure. In this case, null will represent a failure.
Below is an example test class which has been written using JUnit, and a class which in testing jargon is called a Fake:
public class CalculatorControlLogicTest { @Test public void validExpressionTest() { CalculatorContract.IComputationLogic comp = new FakeComputationLogic(); CalculatorContract.IUserInterface ui = new FakeUserInterface(); CalculatorControlLogic controller = new CalculatorControlLogic(ui, comp); controller.handleInput('='); assertTrue(((FakeUserInterface) ui).displayUpdateCalled); assertTrue(((FakeUserInterface) ui).displayValueFinal.equals("10.0")); assertTrue(((FakeComputationLogic) comp).computeResultCalled); } @Test public void invalidExpressionTest() { CalculatorContract.IComputationLogic comp = new FakeComputationLogic(); ((FakeComputationLogic) comp).returnEmpty = true; CalculatorContract.IUserInterface ui = new FakeUserInterface(); ((FakeUserInterface) ui).displayValueInitial = "+7+7"; CalculatorControlLogic controller = new CalculatorControlLogic(ui, comp); controller.handleInput('='); assertTrue(((FakeUserInterface) ui).showErrorCalled); assertTrue(((FakeComputationLogic) comp).computeResultCalled); } private class FakeUserInterface implements CalculatorContract.IUserInterface{ boolean displayUpdateCalled = false; boolean showErrorCalled = false; String displayValueInitial = "5+5"; String displayValueFinal = ""; @Override public void updateDisplay(String displayText) { displayUpdateCalled = true; displayValueFinal = displayText; } @Override public String getDisplay() { return displayValueInitial; } @Override public void showError() { showErrorCalled = true; } } private class FakeComputationLogic implements CalculatorContract.IComputationLogic{ boolean computeResultCalled = false; boolean returnEmpty = false; @Override public Optional computeResult(String expression) { computeResultCalled = true; if (returnEmpty) return Optional.empty(); else return Optional.of("10.0"); } } }
As you can see, not only can this test suite be executed very rapidly, but it did not take very much time at all to write. In any case, we will now look at some more subtle things which made writing this test class very easy.
The Power Of Abstraction And Dependency Inversion
There are two other important concepts which have been applied to CalculatorControlLogic which have made it trivially easy to test. Firstly, if you have ever wondered what the benefits of using Interfaces and Abstract Classes (collectively referred to as abstractions) in Java are, the code above is a direct demonstration. Since the class to be tested references abstractions instead of concrete classes, we were able to create Fake test doubles for the user interface and back end from within our test class. As long as these test doubles implement the appropriate interfaces, CalculatorControlLogiccould not care less that they are not the real thing.
Secondly, CalculatorControlLogichas been given its dependencies via the constructor (yes, that is a form of Dependency Injection), instead of creating its own dependencies. Therefore, it does not need to be re-written when used in a production or testing environment, which is a bonus for efficiency.
Dependency Injection is a form of Inversion Of Control, which is a tricky concept to define in plain language. Whether you use Dependency Injection or a Service Locator Pattern, they both achieve what Martin Fowler (my favourite teacher on such topics) describes as “the principle of separating configuration from use.” This results in classes which are easier to test, and easier to build in isolation from one another.
Testing Computation Logic
Finally, we come to the ComputationLogic class, which is supposed to approximate an IO device such as an adapter to a remote server, or a local database. Since we need neither of those for a simple calculator, it will just be responsible for encapsulating the logic required to validate and evaluate the expressions we give it:
public class CalculatorComputationLogic implements CalculatorContract.IComputationLogic { private final char ADD = '+'; private final char SUBTRACT = '-'; private final char MULTIPLY = '*'; private final char DIVIDE = '/'; @Override public Optional computeResult(String expression) { if (hasOperator(expression)) return attemptEvaluation(expression); else return Optional.empty(); } private Optional attemptEvaluation(String expression) { String delimiter = getOperator(expression); Binomial b = buildBinomial(expression, delimiter); return evaluateBinomial(b); } private Optional evaluateBinomial(Binomial b) { String result; switch (b.getOperatorChar()) { case ADD: result = Double.toString(b.firstTerm + b.secondTerm); break; case SUBTRACT: result = Double.toString(b.firstTerm - b.secondTerm); break; case MULTIPLY: result = Double.toString(b.firstTerm * b.secondTerm); break; case DIVIDE: result = Double.toString(b.firstTerm / b.secondTerm); break; default: return Optional.empty(); } return Optional.of(result); } private Binomial buildBinomial(String expression, String delimiter) { String[] operands = expression.split(delimiter); return new Binomial( delimiter, Double.parseDouble(operands[0]), Double.parseDouble(operands[1]) ); } private String getOperator(String expression) { for (char c : expression.toCharArray()) { if (c == ADD || c == SUBTRACT || c == MULTIPLY || c == DIVIDE) return "\\" + c; } //default return "+"; } private boolean hasOperator(String expression) { for (char c : expression.toCharArray()) { if (c == ADD || c == SUBTRACT || c == MULTIPLY || c == DIVIDE) return true; } return false; } private class Binomial { String operator; double firstTerm; double secondTerm; Binomial(String operator, double firstTerm, double secondTerm) { this.operator = operator; this.firstTerm = firstTerm; this.secondTerm = secondTerm; } char getOperatorChar(){ return operator.charAt(operator.length() - 1); } } }
There is not too much to say about this class since typically there would be some tight coupling to a particular back-end library which would present similar problems as a class tightly coupled to Android. In a moment we will discuss what to do about such classes, but this one is so easy to test that we may as well have a try:
public class CalculatorComputationLogicTest { private CalculatorComputationLogic comp = new CalculatorComputationLogic(); @Test public void additionTest() { String EXPRESSION = "5+5"; String ANSWER = "10.0"; Optional result = comp.computeResult(EXPRESSION); assertTrue(result.isPresent()); assertEquals(result.get(), ANSWER); } @Test public void subtractTest() { String EXPRESSION = "5-5"; String ANSWER = "0.0"; Optional result = comp.computeResult(EXPRESSION); assertTrue(result.isPresent()); assertEquals(result.get(), ANSWER); } @Test public void multiplyTest() { String EXPRESSION = "5*5"; String ANSWER = "25.0"; Optional result = comp.computeResult(EXPRESSION); assertTrue(result.isPresent()); assertEquals(result.get(), ANSWER); } @Test public void divideTest() { String EXPRESSION = "5/5"; String ANSWER = "1.0"; Optional result = comp.computeResult(EXPRESSION); assertTrue(result.isPresent()); assertEquals(result.get(), ANSWER); } @Test public void invalidTest() { String EXPRESSION = "Potato"; Optional result = comp.computeResult(EXPRESSION); assertTrue(!result.isPresent()); } }
The easiest classes to test, are those which are simply given some value or object, and are expected to return a result without the necessity of calling some external dependencies. In any case, there comes a point where no matter how much software architecture wizardry you apply, you will still need to worry about classes which cannot be decoupled from platforms and frameworks. Fortunately, there is still a way we can employ software architecture to: At worst make these classes easier to test, and at best, so trivially simple that testing can be done at a glance.
Humble Objects And Passive Views
The above two names refer to a pattern in which an object that must talk to low-level dependencies, is simplified so much that it arguably does not need to be tested. I was first introduced to this pattern via Martin Fowler’s blog on variations of Model-View-Presenter. Later on, through Robert C. Martin’s works, I was introduced to the idea of treating certain classes as Humble Objects, which implies that this pattern does not need to be limited to user interface classes (although I do not mean to say that Fowler ever implied such a limitation).
Whatever you choose to call this pattern, it is delightfully simple to understand, and in some sense I believe it is actually just the result of rigorously applying SOC to your classes. While this pattern applies also to back end classes, we will use our user interface class to demonstrate this principle in action. The separation is very simple: Classes which interact with platform and framework dependencies, do not think for themselves (hence the monikers Humble and Passive). When an event occurs, the only thing they do is forward the details of this event to whatever logic class happens to be listening:
//from CalculatorActivity's onCreate() function: evaluate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { controlLogic.handleInput('='); } });
The logic class, which should be trivially easy to test, is then responsible for controlling the user interface in a very fine-grained manner. Rather than calling a single generic updateUserInterface(...) function on the user interface class and leaving it to do the work of a bulk update, the user interface (or other such class) will possess small and specific functions which should be easy to name and implement:
//Interface functions of CalculatorActivity: @Override public void updateDisplay(String displayText) { display.setText(displayText); } @Override public String getDisplay() { return display.getText().toString(); } @Override public void showError() { Toast.makeText(this, INVALID_MESSAGE, Toast.LENGTH_LONG).show(); } //…
In principal, these two examples ought to give you enough to understand how to go about implementing this pattern. The object which possesses the logic is loosely coupled, and the object which is tightly coupled to pesky dependencies becomes almost devoid of logic.
Now, at the start of this subsection, I made the statement that these classes become arguably unnecessary to test, and it is important we look at both sides of this argument. In an absolute sense, it is impossible to achieve 100% test coverage by employing this pattern, unless you still write tests for such humble/passive classes. It is also worth noting that my decision to use a Calculator as an example App, means that I cannot escape having a gigantic mass of findViewById(...) calls present in the Activity. Giant masses of repetitive code are a common cause of typing errors, and in the absence of some Android UI testing frameworks, my only recourse for testing would be via deploying the feature to a device and manually testing each interaction. Ouch.
It is at this point that I will humbly say that I do not know if 100% code coverage is absolutely necessary. I do not know many developers who strive for absolute test coverage in production code, and I have never done so myself. One day I might, but I will reserve my opinions on this matter until I have the reference experiences to back them up. In any case, I would argue that applying this pattern will still ultimately make it simpler and easier to test tightly coupled classes; if for no reason other than they become simpler to write.
Another objection to this approach, was raised by a fellow programmer when I described this approach in another context. The objection was that the logic class (whether it be a Controller, Presenter, or even a ViewModel depending on how you use it), becomes a God class.
While I do not agree with that sentiment, I do agree that the end result of applying this pattern is that your Logic classes become larger than if you left more decisions up to your user interface class.
This has never been an issue for me as I treat each feature of my applications as self-contained components, as opposed to having one giant controller for managing multiple user interface screens. In any case, I think this argument holds reasonably true if you fail to apply SOC to your front end or back end components. Therefore, my advice is to apply SOC to your front end and back end components quite rigorously.
Further Considerations
After all of this discussion on applying the principles of software architecture to reduce the necessity of using a wide-array of testing frameworks, improve the testability of classes in general, and a pattern which allows classes to be tested indirectly (at least to some degree), I am not actually here to tell you to stop using your preferred frameworks.
For those curious, I often use a library to generate mock classes for my Unit tests (for Java I prefer Mockito, but these days I mostly write Kotlin and prefer Mockk in that language), and JUnit is a framework which I use quite invariably. Since all of these options are coupled to languages as opposed to the Android platform, I can use them quite interchangeably across mobile and web application development. From time to time (if project requirements demand it), I will even use tools like Robolectric, MockWebServer, and in my five years of studying Android, I did begrudgingly use Espresso once.
My hope is that in reading this article, anyone who has experienced a similar degree of aversion to testing due to paralysis by jargon analysis, will come to see that getting started with testing really can be simple and framework minimal.
Further Reading on SmashingMag:
Sliding In And Out Of Vue.js
Designing And Building A Progressive Web Application Without A Framework
CSS Frameworks Or CSS Grid: What Should I Use For My Project?
Using Google’s Flutter For Truly Cross-Platform Mobile Development
Tumblr media
(dm, il)
0 notes
Text
If you’re learning Java as a first programming language, try learning another programming language with it
TLDR; Try your hardest to learn another language alongside Java via the internet like Python or basic C syntax, just so that way you don’t get discouraged by the initial complexity of Java or the initial learning curve associated with OOP.
Alright so as the title of the post suggests, if you’re a new CS student at whatever University or College you’re attending you’ll probably learn Java as a first language. Java’s a great language for stack developers or people that have to actively work with SQL or anything related to servers. But it’s a really piss-poor language to learn with, and here’s why.
1) It forces Object Orientation:
I myself recall my first ever CS class: AP Computer Science. I was in the class with a lot of people much smarter than me when it came to Math and Science, and who were just generally more clever than I. However, our first language was unfortunately Java. The poor kids were forced day one into trying to understand a rather complex topic of Computer Science. A few dropped out and their dreams of becoming programmers halted; all because they weren’t properly exposed to the rest of the programming industry.
2) Java is very verbose and just confuses the shit out of people:
Now this topic is a bit funny because I really like Rust and C++ as programming languages and they’re similar in this regard. But the matter still stands. A lot of people are going to find that trying to follow Java code is somewhat difficult, which is due mostly to Object Orientation. For instance, every function has to be declared within a class: if you want to group a certain type of functions together in a package, they have to be in a class file and made static. Coming from languages like Rust, C++, and Python, this is a really stupid way to behave.  
3) Java code can be pretty hard to read:
I touched on this with reason 2, but there are other reasons as well as to why. There’s an opinion among certain developers that Object Orientation is very forgiving when it comes to coding style. I happen to agree with this opinion, and I will be using it for this post. Object Orientation itself isn’t a problem if you have a well trained, motivated individual who tries to focus good programming style. Unfortunately, everyone has bad programming style at times, and you can see it in large programs or open source software. Java, which was (and still is for the most part) taught as a primary starting language for students, forces Object Orientation on students. So, many people will want to try writing programs that test their skill and ability, mixing in their terrible coding styles (after all, we all need to learn by doing, right?). This leaves a huge codebase for people to learn Java from, but at the same time most of it is illegible and just plain terrible. No language is free from the curse of spaghetti code, and it seems that many up and coming Java developers find it very, very easy to write spaghetti code
4) Java is not general purpose:
I understand full and well that Java has a lot of built in functions that allow for general purpose programming (built in JFrames, etc etc) and has all the fun bloat associated with those features. However, it isn’t a proper indicator of what the industry uses as general purpose. Something like Python is used far more for “General Purpose” programming needs. 
In summary, Java holds a close place next to my heart, as well as the hearts of many developers. However, from a pure “Just getting started” mindset, go learn another language on the side. It doesn’t do you any justice if a complex subject of programming discourages you to the point of not wanting to program anymore.
1 note · View note
pixelpocalypsed · 8 years ago
Text
Tutorials Are Hard
Tumblr media
Making tutorials for games is a real pain. Typically, by the time you start working on the tutorial you are already most of the way through the process of making the game.  After all, it’s no good spending a lot of time making the tutorial only to find out that you will be changing the core rules of the game.
This leaves you with a difficult choice; should you write all new game classes with redundant code, or should you alter your existing game classes in order to add the tutorial behavior? The latter is usually better because if you simply copy and paste the required parts of your game, you will need to apply any updates or bug fixes to two different sets of classes; the tutorial ones and the regular ones.
Back when I was working on VoxelCity, I was determined to release the game by a specific date.  As the date approached, I realized due to player feedback that I needed to have an interactive tutorial.  Players do not learn your game by reading, but rather need to actively participate in the learning process in order to adequately learn how to play.  As a result of the tight deadline, I butchered my GameScreen class, shoving the tutorial code into all the places it was needed.  I successfully got things working and got the game released, but I always felt really bad about needing to add a large amount of code directly to the existing game in order to hack in the functionality.
For Castleparts, I did things differently.  I planned for the need for a tutorial, and I made sure to have methods in the GameScreen class that I could override in order to handle the tutorial events. I posted the resulting TutorialGameScreen here. Go take a look:
http://codereview.stackexchange.com/questions/127706/game-tutorial-in-java
Today I got to reap the rewards of this approach when I went back to the tutorial and expanded it in order to have tutorials for the two other game modes available for play in Castleparts.  Unfortunately I did have to modify the original GameScreen class a bit in order to get things to work perfectly. However, after doing that, adding new sections of the tutorial was as simple as adding new TutorialScreen classes for the other types of games I wanted to explain, and adding new enums for the different text I wanted to have for those other game types.
In short, my advice is to:
Extend your existing game rather than creating a whole separate game module just for the tutorial.
Extend your top level Game class with specialized Tutorial classes rather than hacking in the tutorial code. If you don’t, you will feel bad and your code will look like bad spaghetti.
Realize that no matter what you do, if your game is complex you will need to modify the original top level Game class in order to accomodate the tutorial.
Regarding the last point, Castleparts is a pretty complicated game with lots of different pieces working together to create the finished product.  Something as small as not having the talking heads appear during the Tutorial mode was impossible to do without editing the existing GameScreen class. I also wanted to prevent the AI from making moves for the first parts of the tutorial, but let them resume making moves later.
The lesson here is that the more modular you make your code/game, and the more you use composition rather than inheritance, the easier it will be for you to combine things together in different ways when necessary.  However, it is much better to actually finish the work than to endlessly try to figure out the best way to do things. When programming you always have to make a tradeoffs, and here the tradeoff was to go with a slightly less clean approach to save the huge amount of time it would take to re-code everything to be completely modular.  The players of your game will not see the code, and they are only going to play the tutorial once anyway. If they play it at all!
To get regular updates about Castleparts, follow me on twitter:
https://twitter.com/pixelpocalypsed
0 notes