tag:blogger.com,1999:blog-35122509942701595122024-03-08T23:49:47.710+00:00Andy PThis blog has moved to <a href="http://andypalmer.com" rel="me">andypalmer.com</a>Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.comBlogger12125tag:blogger.com,1999:blog-3512250994270159512.post-7887705714075474012008-10-24T14:58:00.002+01:002008-10-24T15:03:52.677+01:00Moving Swiftly AlongMy blog is relocating to http://andypalmer.com<br /><br />If you are currently subscribed directly to this blog, please update your feed url to http://feeds.feedburner.com/andypalmer/blog<br /><br />I have let the aggregators know the new feed, but it may take some time for this to filter through. Articles will be cross posted for a short changeover period.<br /><br />See you all at the new placeAndyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com0tag:blogger.com,1999:blog-3512250994270159512.post-32828405117135143192008-09-06T16:38:00.002+01:002008-09-06T17:56:15.454+01:00(I can't get no) Satisfaction<style type="text/css"><br />quote.lyrics {font-size:small}<br />span.lyrics-info {font-size:small, font-style:italic}<br /></style><br /><quote class="lyrics"><br />I can't get no satisfaction, I can't get no satisfaction<br />'Cause I try and I try and I try and I try<br /><br /><span class="lyrics-info">(I Can't Get No) Satisfaction - The Rolling Stones</span><br /></quote><br /><br /><h3>Checked vs. Unchecked Exceptions</h3><br />It's a controversial subject; searching Google for <a href="http://www.google.co.uk/search?q=checked+versus+unchecked+exceptions">checked versus unchecked exceptions</a> reveals a lot of heated discussions both for and against Checked Exceptions.<br /><a href="http://www.cogentdude.com">Charles Lowell</a> has a particularly <a href="http://www.cogentdude.com/checked-vs-unchecked.html">succinct (and possibly NSFW) view</a><br /><br />My view is similar. I can <i>almost</i> see a situation where I would like to ensure that exceptions are dealt with in a certain way, but it doesn't quite reach as far as using checked exceptions.<br /><br /><b>Noisy IDEs</b><br /><br />I think my issue is largely to do with the way that the IDEs (and compilers) deal with checked exceptions.<br />In Eclipse, if a method throws a checked exception, then auto-fix suggests two options; "Surround with try/catch" and "Add throws declaration to method"<br />I particularly have a problem with the second one. The further you get away from the source of the exception, the less likely you are to deal with it in a useful way. Not dealing with the exception is a reasonable option, but why does everyone in the call chain need to know about an exception that someone in the lower levels didn't care enough about to handle?<br />Using unchecked exceptions gives us the choice to handle or propagate the exception, without polluting the higher levels with knowledge of unhandled exceptions.<br /><br />The default Eclipse template for try / catch is to catch the exception and print the stack trace. This isn't really <i>handling</i> the exception, it's <i>ignoring it</i> (even if we've logged it). There is no way to know if this is an acceptable default without understanding the underlying code. Unfortunately, if we are leaving it as the default we probably don't understand the underlying code (yet).<br /><br /><b>An alternative to Checked Exceptions</b><br />One of the nice things about checked exceptions is that they let you know the types of exception you can get. You get the IDE auto-completion and compiler checking.<br />A checked exception is essentially saying, "You might get this exception, and I <b>insist</b> that you deal with it in the way that you feel is most appropriate"<br />How can we continue to do this, and at the same time prevent boiler-plate or unnecessary pollution?<br /><br />Something that I'm thinking about (but haven't had the need to implement yet), is the concept of an ExceptionHandler class. This is an ideal place for Java Generics to come into play.<br />For example:<br /><code class="prettyprint"><br />public void someCodeThatMayThrowAFileNotFoundException(String fileName, ExceptionHandler<FileNotFoundException> exceptionHandler) {<br /> try {<br /> open(fileName); // throws FileNotFoundException<br /> }<br /> catch (FileNotFoundException e) {<br /> exceptionHandler.handleException(e);<br /> }<br />}<br /></code><br /><br />By coding in this way, we can provide sensible default actions, such as:<br /><code class="prettyprint"><br /><br />// The Generic stuff has not been checked and is for illustration purposes.<br />// Let me know if I need to correct it :-)<br />public class PropagatingExceptionHandler extends ExceptionHandler<T extends Exception> {<br /> public void handleException(T exception) {<br /> throw(exception);<br /> }<br />}<br /><br />public class LogAndIgnoreExceptionHandler extends ExceptionHandler<T extends Exception> {<br /> public void handleException(T exception) {<br /> exception.printStackTrace(); // or log4j equivalent<br /> }<br />}<br /></code><br /><br />This reduces the try / catch noise, gives us the compiler checking and type completion, and explicitly details the strategy that we are using for handling the exceptions for this part of code.<br /><br />I'm open to someone showing me a really good use of checked exceptions, but for the time being, I won't be using them.Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com2tag:blogger.com,1999:blog-3512250994270159512.post-60545375908342663392008-08-30T12:11:00.004+01:002008-09-06T16:36:14.883+01:00What should my code tell me?When I'm reading code, there are a number of things that make my job much easier.<br />The most important (in my opinion) is good naming, but how can we choose suitable names?<br />This is the pattern that I am starting to see (and recommend), and I think it is pretty effective.<br /><br /><h3>What? How? Who? Why?</h3><br />If we think of the code as a medium of communication, what questions should the code be able to answer?<br /><br /><b>What are we trying to achieve?</b><br />The method name should be a summary of what we are trying to achieve. For example, <code class="prettyprint">public void selectTheCustomerNamed(String customerName) {...}</code><br />It is (hopefully) clear from the method name what will happen when we call it, and what we need to pass in in order for the code to work.<br /><br /><b>How do we do this?</b><br />The body of the method should be the steps needed to achieve what it is we said we would do in the method name. The steps should be at the same level of abstraction. (See <a href="http://c2.com/ppr/wiki/WikiPagesAboutRefactoring/ComposedMethod.html">Composed Method</a>)<br /><code class="prettyprint"><br />public void selectTheCustomerNamed(String customerName) {<br /> theUser.clicksOn(Application.SEARCH_BUTTON);<br /> theUser.types(customerName,into(SearchPage.CUSTOMER_NAME));<br /> theUser.clicks(SearchPage.BEGIN_SEARCH);<br /> theUser.clicks(ResultsPage.FIRST_RESULT);<br />}<br /></code><br /><br /><b>Who is doing the work?</b><br />In the example above, by naming the receiver of the messages after the actor who is likely to perform these steps, it helps focus the mind, and can help with appropriate method naming. For example, we can ask ourselves "What does theUser need to do next?"<br /><br /><b>Why are we doing this?</b><br />At one time or another, you will probably come across a comment like this:<br /><code class="prettyprint"><br />// Add one to foo<br />foo++;<br /></code><br />This kind of comment is irrelevant at best, it is telling us <i>what</i> the next line is doing. If the methods are well named, or the code is idiomatic, this is unnecessary. One negative consequence may be a kind of "comment blindness" where your mind filters the majority of comments as "noise".<br />A better use of comments is to explain <i>why</i> we are doing things in a certain way, especially if it seems unusual. <a href="http://martinfowler.com">Martin Fowler</a> briefly mentions this in the "Code Smells" chapter in <a href="http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672">Refactoring</a>.<br />An example of this type of comment is shown below. This represents a hard-won piece of knowledge, and will hopefully prevent a well-meaning programmer from replacing the code with the <code>getFooByQuery</code> variant without checking the caching behaviour.<br /><code class="prettyprint"><br />// We query the id first before trying to retrieve the object, because if we try to<br />// retrieve the object directly (with getFooByQuery), it always bypasses <br />// the cache (see @link{...})<br />Integer fooId = findFooIdByQuery(query);<br />return getFooById(fooId);<br /></code><br /><br />JavaDoc can also be used to tell your users <i>why</i> they might need to pay special attention. If the code is well named, you are unlikely to need JavaDoc for all classes.<br />Here are some examples of where you might like to use JavaDoc:<br /><ul><br /><li>If you have a number of pluggable modules, why would you want to use this module</li><br /><li>Reasons why a user might want to provide their own implementation of this class</li><br /><li>Reasons why this code throws exceptions, and why a user might want to handle them</li><br /></ul><br /><br />This is still a work in progress, but the results that I have seen so far have been encouraging. Please let me know in the comments if you have any suggestions for refinements or improvements.Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com0tag:blogger.com,1999:blog-3512250994270159512.post-44456171338449825662008-08-25T11:47:00.003+01:002008-08-25T12:09:43.543+01:00Well, how did I get here?<quote><br />And you may find yourself living in a shotgun shack<br />And you may find yourself in another part of the world<br />And you may find yourself behind the wheel of a large automobile<br />And you may find yourself in a beautiful house, with a beautiful wife<br />And you may ask yourself, "Well… how did I get here?"<br /><br /><i>Once in a Lifetime - Talking Heads</i><br /></quote><br /><br /><h3>Logging a Stack Trace in log4j</h3><br /><br />A while ago, I was trying to replace a number of hard coded strings with an appropriate Enum. Unfortunately, the design of the code I was working on meant that it was difficult to find all the possible values for the strings.<br />I decided that the behaviour that I wanted was:<br /><ul><br /><li>If the string method was called with a new value, log that value, so that I can make a decision about whether I wish to add it to the Enum</li><br /><li>If the string method was called with a value that matched a value in the Enum, I wanted a stack trace, so I could replace that call with the appropriate Enum call</li><br /></ul><br /><br />The code I ended up with was something like this:<br /><code class="prettyprint"><br />public void doSomething(String theDestination) {<br /> if(weKnowAbout(theDestination)) {<br /> logger.debug("doSomething called with a known destination of " + theDestination, new Throwable());<br /> }<br /> else {<br /> logger.debug("doSomething called with unknown destination of " + theDestination);<br /> }<br /> doWhatWeNeedToDoWith(theDestination);<br />}<br /></code><br /><br />Passing the <code>new Throwable()</code> into log4j creates a stack trace in the log output. I've found a number of places that tell you how to do this (now that I know how to do it), but it took me quite a bit of searching when I was first looking.Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com0tag:blogger.com,1999:blog-3512250994270159512.post-91843141852968990772008-08-11T22:03:00.002+01:002008-08-11T22:05:08.997+01:00ILikeThisBlogPost<a href="http://www.testingreflections.com/node/view/7234">ILikeThisBlogPost</a> - but then I'm a little biased :-)Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com0tag:blogger.com,1999:blog-3512250994270159512.post-84552305907149322702008-08-10T19:00:00.004+01:002008-08-10T19:25:53.099+01:00Returning 'null' Considered Dishonest<h3>Background</h3><br /><a href="http://www.testingreflections.com/blog/2">Antony Marcano</a> and I have just started running a coding design workshop. Most of the audience are new to coding and we are trying to focus on good habits that are applicable across all programming languages.<br />In our first session, we created a vending machine. By the end of 90 minutes, it was able to dispense a drink as long as it had sufficient money and stock available.<br />One of the questions that we asked was "What do we do when the customer has not inserted enough money and we press the button for the drink?"<br />Some of the people who had some programming background said "Return null", which is what brings us to this post.<br /><br /><h3>Good Citizen</h3><br />In the wiki for the PicoContainer project, there is a page titled "<a href="http://docs.codehaus.org/display/PICO/Good+Citizen">Good Citizen</a>", which details a number of good practices that all Java classes would benefit from trying to adhere to.<br />The practices make a lot of sense when you think about them, but they aren't really explained. I'm going to try and address that issue as we cover them in the workshop.<br />The practice that we are looking at today is <i>"Never expect or return null"</i>, with a dash of <i>"Fail Fast"</i> for flavour.<br /><br /><h3>What's so bad about null?</h3><br />In the Java world, when we declare a method signature, we specify a return type. In the case of our vending machine we had:<br /><code class="prettyprint"><br />public Drink giveMeADrink() {...}<br /></code><br />By declaring that the return type is <code>Drink</code>, we are signing up to return something that <i>"is-a"</i> drink.<br />We <i><b>could</b></i> also return <code>null</code>, which is a nothing value. We <i><b>could</b></i> use this to represent that you did not get a drink.<br />The client code may look something like this:<br /><code class="prettyprint"><br />myVendingMachine.giveMeADrink().downInOne();<br /></code><br />If we return <code>null</code>, this code will fail with a <code>NullPointerException</code>. Not particularly useful, but at least we are using the result straight away. The problems become much worse if we store the returned <code>Drink</code> for use later.<br />When we said we will always return a <code>Drink</code>, we <b>lied</b>.<br /><br /><h3>Programming Defensively</h3><br />The sample client code above makes the assumption that the result of <code>giveMeADrink</code> will be a <code>Drink</code>. Given that we've actually signed up to that contract, that doesn't seem to be unreasonable. But now the client code is broken and they have an angry customer, they are going to have to work around the issue. It would probably look like this:<br /><code class="prettyprint"><br />Drink myDrink = myVendingMachine.giveMeADrink();<br />if(myDrink != null) {<br /> myDrink.downInOne();<br />}<br /></code><br />This code is actually saying "I've asked you to give me a drink, <b>but I don't trust you</b>, so I will check first".<br /><br /><h3>Why isn't this working? An Exceptional Approach</h3><br />If we rely on our client to check that they received a valid result, we lose out on an opportunity to let the client know why the call was unsuccessful.<br />In the Programming Defensively example, we can <i>recover</i> from being returned a <code>null</code>, but we don't know <i>why</i> it was <code>null</code>. Was it because we hadn't inserted the money? Was it because the machine was out of stock? Was it because the stars were out of alignment?<br />Do we handle the scenarios differently? If we haven't inserted enough money, that's something we can deal with, but if the machine is empty, we need to look for another machine.<br /><br />What if our code looked like this?<br /><code class="prettyprint"><br />public Drink giveMeADrink() {<br /> if(weDontHaveEnoughMoney()) {throw new NotEnoughMoneyException();}<br /> if(weDontHaveEnoughStock()) {throw new DrinkOutOfStockException();}<br /> return new Drink();<br />}<br /></code><br />What we have said is "We will always give you a drink <i>or tell you why we couldn't</i>"<br /><br />Now when we attempt to call <code>giveMeADrink</code>, it lets us know straight away if it can't proceed. It also gives us a good indication of <b>why</b> it is having problems.<br />The client code calls:<br /><code class="prettyprint"><br />myVendingMachine.giveMeADrink().downInOne();<br /></code><br />and gets told "Sorry, I'd give you a drink, <b>but you didn't insert enough money</b>".<br />Our code is being honest, polite and giving the client an opportunity to remedy the situation. The customer is still angry, but now he's angry with himself for not putting in enough money.<br /><br /><br /><h3>In Summary</h3><br /><ul><br /><li>Programming defensively is programming distrustfully</li><br /><li>Returning null is dishonest. It requires others to check that we've upheld our side of the bargain</li><br /><li>Throwing a meaningful exception allows us to let the caller know <i>why</i> their call did not succeed</li><br /></ul>Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com15tag:blogger.com,1999:blog-3512250994270159512.post-35468660669544976582008-05-22T17:43:00.003+01:002008-05-22T18:01:06.921+01:00SingletonsI've been playing about with a code base that has a large number of singletons, for what appears to be no apparent reason.<br />Something about singletons doesn't sit quite right with me, but in the most part, if they're not misbehaving, I'll probably leave them be.<br /><br />The thing that bothers me a lot more though, is the fact that everyone else is forced to know about your singleton-ness..<br /><br /><code>ImASingleton.getInstance().doSomething();</code><br /><br />*shivers*<br /><br />If you have to store global state, it would be nice if we could hide that from the clients. Why do we have a single instance with instance variables accessed through a static getInstance method, when we could hide (ooh, encapsulation) the implementation behind multiple instances with private static variables?<br />By using instance methods, we also get all the interface / testing / mocking goodness.<br /><br /><code>new ImASingletonButYouCantTell().doSomething();</code><br /><br />OK, I'm rambling... am I missing something? (please rant below)Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com2tag:blogger.com,1999:blog-3512250994270159512.post-90941958714583887532008-05-21T10:27:00.002+01:002008-05-21T10:52:46.606+01:00Static Utility MethodsA few of us were chatting about static methods the other day.<br />I'm not a big fan. I think that they tie you unnecessarily to a concrete class.<br /><br />Most people were saying that there is no harm in having static methods in utility classes, and this is one place where I would disagree.<br />The example used was the java.lang.Math class with it's utility methods.<br /><br />java.lang.Math is non-instantiable, you can only access it's utility methods through static calls.<br />For the most part, this is fine, but take the example of Math.sqrt(x)<br />This method returns NaN if x is < 0. For the majority of cases this is fine, but if I start working with complex numbers, then this is no longer the ideal behaviour.<br />Admittedly, in this case I would not want the return value to be a double, but I may want it to throw a ComplexNumberException or something similar. If I have used the static method throughout my code, I now have to go and update every single reference to the concrete class. What a pain in the class!<br /><br />However, if I had used a utility class with instance methods, I could have injected the utility class in the constructor and I could now change it out for my new ComplexMathUtility class through configuration / DI.<br /><br />This isn't the best example, as the required return types are different, but think of a String utility class, perhaps something that formats a header for a report.<br />We could (naively) implement this using static utility methods. If we later require two different types of report heading, this is going to make our life difficult.<br />If we implement this using an instantiable ReportFormatter, we can swap in new ones at runtime / configuration. We've just implemented a ReportFormatterStrategy :-)<br /><br />My feeling is that implementing utilities as instance methods keeps the design flexible and allows for more code reuse as things naturally gravitate towards smaller, more easily testable, logically grouped units of functionality...<br />Or am I just dreaming? :-)Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com2tag:blogger.com,1999:blog-3512250994270159512.post-39549861212451798622008-02-09T19:24:00.002+00:002008-08-10T17:56:22.887+01:00Refactoring / Design: Composed MethodI'm going to try and write a few posts over the next few weeks around the subject of Refactoring and Design. This is mainly practice for me, so that when asked to explain, I don't confuse the issue. All comments and suggestions are gratefully accepted.<br /><hr /><br /><br />When I am looking at code, I feel a lot more comfortable if a method is composed of several steps of similar context, in the appropriate order.<br /><br />This is the Composed Method pattern from <a href="http://tinyurl.com/ytj4bb">Smalltalk Best Practice Patterns</a>. <br />In <a href="http://tinyurl.com/2h85lm">Refactoring To Patterns</a>, Joshua Kerievsky says that this is one of the most important refactorings that he knows, and I would have to agree.<br /><br />Here is an example that I just thought of (see if you can guess what I was doing):<br /><br /><pre class="prettyprint"><br />public class CheeseCake {<br /> <br /> private final Person chef;<br /> private final Sink kitchen;<br /> private CakeTin<CheeseCake> cake;<br /> <br /> public CheeseCake (Person chef, Sink sink) {<br /> this.chef = chef;<br /> this.sink = sink;<br /> }<br /> <br /> public makeBase(Ingredients... ingredients) {<br /> sink.runUnderWater(chef.getHands());<br /> sink.useSoap(chef.getHands());<br /> sink.dry(chef.getHands());<br /> Bowl<Ingredients> bowl = new Bowl<Ingredients>();<br /> for (Ingredients i : ingredients) {<br /> bowl.add(i);<br /> }<br /> bowl.mix();<br /> CakeTin<CheeseCake> cake = new CakeTin<CheeseCake>();<br /> cake.add(bowl.contents());<br /> }<br /><br />}<br /></pre><br /><br />Wow, we have quite a few smells in this code, and not the pleasant smells of cheesecake cooking either...<br />We'll ignore the Feature Envy and things for the moment, but I've only just written this code and already I'm having trouble telling what it does at a glance...<br /><br />What about if the makeBase method looked like this:<br /><br /><code class="prettyprint"><br />public makeBase(Ingredients... ingredients) {<br /> washHands();<br /> Bowl<Ingredients> bowl = addTo(new Bowl<Ingredients>(), ingredients);<br /> bowl.mix();<br /> cake.add(bowl.contents(); // Moved CakeTin creation into constructor<br />}<br /></code><br /><br />That looks much better, but not only that, we also find that the washHands() method is used extensively. Now each step in the recipe doesn't need to implement their own hands washing routine.<br /><br />It's also much more obvious that washHands has absolutely nothing to do with CheeseCake, and much more to do with either Person or Sink... maybe we'll look at this next time :-)Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com2tag:blogger.com,1999:blog-3512250994270159512.post-89342075340639306352007-11-24T21:28:00.000+00:002007-11-24T21:53:32.944+00:00Things I Like: Mercurial Version ControlI was recently introduced to Mercurial version control. (<a href="http://www.selenic.com/mercurial/">http://www.selenic.com/mercurial/</a>)<br />I haven't had enough time to play with it to tell how well it holds up in all situations, but first impressions are very positive.<br /><br />Things I like (a lot):<br />- the repository is contained within the working directory, but only in the root, so you don't have version control artifacts spread throughout the directory structure.<br />- The way it handles development branches is very clever. Modifications are stored as changesets, which are then applied to a common parent. If two developers move down separate paths, it effectively creates two branches. It's possible to change between "branches" by reverting to the common parent and then applying the changesets for the other branch.<br />- It's possible to merge the two branches (called heads) together to return to a single main branch (There is a clever pattern that <a href="http://dannorth.net/">Dan North</a> showed me using this that lets you merge a complex change back into another version control system)<br /><br />As an example, here is how you would create a new repository once you have decided that you are going to bring it under version control (before you start, right?) :-)<br /><br />We start in the working directory of the project:<br />- hg init <i>(tell Mercurial to create a repository)</i><br />- hg addremove <i>(add all files in this directory and subdirectories to the repository)</i><br />- hg ci -m "Created repository for Project X" <i>(commit the changes to the repository)</i><br /><br />The ease with which you can create a new repository along with the fact that it is stored alongside the working copy without all the versioning artifacts mean that Mercurial is going to be my version control system of choice for my personal projects going forward (sorry subversion)Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com0tag:blogger.com,1999:blog-3512250994270159512.post-77721597341893802132007-10-14T21:50:00.000+01:002007-10-14T22:03:10.267+01:00A couple of small touchesSometimes it's the small touches that make the difference between an OK piece of software and a great piece of software.<br />A couple of small touches have stood out to me over the last couple of days.<br />Firstly, I've just loaded Gutsy Gibbon onto my laptop. In an office full of techy practical jokers it's very important to lock your screen if you wander away for a few minutes. One of the options in the password entry dialogue for Gutsy is to leave a message for the person who has locked the terminal. This is cool :-)<br /><br />Secondly, I was driving home this evening when the traffic came to a very sudden stop in front of me. I had to hit the brakes hard, and while I diverted my full attention to avoiding a collision with the car in front, I noticed out of the corner of my eye that the car (Renault Laguna) had started flashing the hazard lights.<br />This indicated to the people behind me that this was not an ordinary circumstance, giving them maybe a little extra warning. Overall, this car has given me a fair amount of trouble, but I liked this.Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com0tag:blogger.com,1999:blog-3512250994270159512.post-85011952921795324442007-10-13T20:37:00.000+01:002007-10-13T20:50:21.373+01:00First WeekSo, my first week at Thoughtworks is over.<br />It has been really intense, and I am exhausted.<br />Monday was the normal admin stuff that you deal with on your first day at any company.<br />Tuesday, I got assigned to a beach project (an internal non-billable project) and also asked to help out with some server migration for another project.<br />At least I have managed to deliver some value this week, which is nice.<br /><br />Tuesday evening was Ruby Tuesday, which was interesting (and pizza was provided). You can do some crazy things with Ruby. One of the most interesting was a demonstration of defining methods on Numeric in order to convert units of length.<br /><br />I don't think I got back home much before 9pm most days, and my poor couch potato legs have had to cope with walking for an hour each day.<br /><br />There is a lot of energy in the TW office. You can almost feel it buzzing. So, even though I am tired, I have had a really good time.Andyhttp://www.blogger.com/profile/15414690619832854980noreply@blogger.com1